Consider some test data: a grid of x, y, and values of some function on this grid:
func[x_, y_] =
Exp[-((y - 5)/(10.*Cos[x]^4))] ((Sin[x] Cos[x]^5)/y^2 + Sin[x]^2/(
y^6 + 1) + Sin[x]*Cos[x]^4);
gridx = Table[10^x, {x, -5., Log10[0.2], (Log10[0.2] + 5.)/30}];
gridy = Table[10^
x, {x, Log10[0.05], Log10[150], (Log10[150] - Log10[0.05])/80}];
{xminmax, yminmax} = MinMax[#] & /@ {gridx, gridy};
grid = Tuples[{gridx, gridy}];
distrvals = func @@@ grid;
data = Join[grid, Partition[distrvals, 1], 2];
Having this tabulated data, I want to make a fast interpolation for the random points belonging to the domain defined by the x,y
grid:
nev = 10^6;
{xrand, yrand} = RandomReal[#, nev] & /@ {xminmax, yminmax};
randompts = Join[Partition[xrand, 1], Partition[yrand, 1], 2];
The straightforward but slow way is to use built-in Interpolation
:
intBuiltIn[x_, y_] =
Exp[Interpolation[Log[data], InterpolationOrder -> 1][Log[x],
Log[y]]]
vals1 = intBuiltIn @@@ randompts; // AbsoluteTiming
{4.15255,Null}
Here, I interpolated the logarithmized data to account for the structure of the initial grid and the exponential behavior of the initial function. This way is very slow, so I want to speed up it. I implement a bilinear compilable interpolation:
linint =
Compile[{{xlogrand, _Real}, {ylogrand, _Real}, {gridxlog, _Real,
1}, {gridylog, _Real,
1}, {nearestindexx, _Integer}, {nearestindexy, _Integer}, \
{distrlog, _Real, 1}},
Module[{xel, yel, x1, x2, y1, y2, z11, z21, z12,
z22, \[CapitalDelta], leny, indexlargerx, indexlargery},
(*Finding the index of the grids with the elements larged than the \
given random point*)
xel = Compile`GetElement[gridxlog, nearestindexx];
indexlargerx = nearestindexx + UnitStep[xlogrand - xel];
yel = Compile`GetElement[gridylog, nearestindexy];
indexlargery = nearestindexy + UnitStep[ylogrand - yel];
(*The two points x1, x2 with x1 < x < x2*)
x1 = Compile`GetElement[gridxlog, indexlargerx - 1];
x2 = Compile`GetElement[gridxlog, indexlargerx];
y1 = Compile`GetElement[gridylog, indexlargery - 1];
y2 = Compile`GetElement[gridylog, indexlargery];
(*Corresponding values of the function*)
leny = Length[gridylog];
z11 =
Compile`GetElement[
distrlog, (indexlargerx - 2)*leny + indexlargery - 1];
z21 =
Compile`GetElement[
distrlog, (indexlargerx - 1)*leny + indexlargery - 1];
z12 =
Compile`GetElement[
distrlog, (indexlargerx - 2)*leny + indexlargery];
z22 =
Compile`GetElement[
distrlog, (indexlargerx - 1)*leny + indexlargery];
\[CapitalDelta] = (x2 - x1) (y2 - y1);
Exp[(*{xlogrand,x1,x2,ylogrand,y1,y2,z11,z21,z12,
z22,*)((x2 - xlogrand)*(y2 - ylogrand))/\[CapitalDelta]*
z11 + ((xlogrand - x1)*(y2 -
ylogrand))/\[CapitalDelta] z21 + ((x2 - xlogrand) (ylogrand -
y1))/\[CapitalDelta]*
z12 + ((xlogrand - x1) (ylogrand - y1))/((x2 - x1) (y2 - y1))
z22(*}*)]
], CompilationTarget -> "C", RuntimeOptions -> "Speed",
RuntimeAttributes -> {Listable}, Parallelization -> True]
and then use Nearest
:
(*Logarithmized grids and random points*)
{gridxlog, gridylog, xlogrand, ylogrand, distrlog} =
Log[#] & /@ {gridx, gridy, xrand, yrand,
distrvals}; // AbsoluteTiming
(*Finding the element of the grid which is nearest to the random \
points*)
nearestx = Nearest[gridxlog -> "Index"];
nearesty = Nearest[gridylog -> "Index"];
nearestindexx = nearestx[xlogrand] // Flatten; // AbsoluteTiming
nearestindexy = nearesty[ylogrand] // Flatten; // AbsoluteTiming
Once I create nearestx
, nearesty
(this has to be done only once), the approach is more than 100 times faster than with using the built-in interpolation:
vals2 = linint[xlogrand, ylogrand, gridxlog, gridylog, nearestindexx,
nearestindexy, distrlog]; // AbsoluteTiming
Abs[(vals2 - vals1)/vals1] // Max
{0.0328157,Null}
1.77041*10^-14
What may be improved in this algorithm? Can it be made faster?
Nearest
's output. Not sure whether that will be faster. $\endgroup${x,y,z,pdf[x,y,z]}
and want to map it topdf[X,xrand,yrand]
, whereX
is some point within the domain of definition ofx
andxrand
,yrand
are random points, would it be efficient to use first one of the methods from the question mathematica.stackexchange.com/questions/282637/… to mapx,y,z,pdf[x,y,z]
$\to$y,z,pdf[X,y,z]
and then the interpolation from this question? $\endgroup$mass
having the tabulated angle-energy distribution{mass, polar angle, energy, distribution}
, that describes the probability of their production at any experimental facility. In the very first step, I generate the angles and energies using the importance sampling method. For this, I first uniformly generate angles and energies within the range interesting to me, and then evaluate the selection weights using the custom interpolation. $\endgroup$