2
$\begingroup$

I have the following problem: I would like to generate random values of some variable $z$ obeying some distribution, but simultaneously within the interval zmin<z<zmax. Below, there is the inverse CDF for this distribution, myInvCDF (it depends on parameter ldecay), the interval, and the CDF range uzmin,uzmax corresponding to the interval:

myInvCDF[u_, ldecay_] = 
  z /. Solve[1 - Exp[-(z/ldecay)] == u, z][[1]] /. {C[1] -> 0};
zmin = 14;
zmax = 34;
uzmin[ldecay_] = u /. Solve[myInvCDF[u, ldecay] == zmin, u, Reals][[1]];
uzmax[ldecay_] = u /. Solve[myInvCDF[u, ldecay] == zmax, u, Reals][[1]];

This is the distribution in ldecay up to some coefficient coef (ldecay[[i]]=coef*EnergyList[[i]]):

EnergyList = Abs[RandomVariate[NormalDistribution[1, 5], 10^6]];

I start with the following test table returning the value of ldecay[[i]], uzmin, uzmax for this ldecay, and evaluation of the condition If[uzmin[coef*EnergyList[[i]]] > 0.9999999,zmin,1]:

TestIf = Hold@
     Compile[{coef, {EnergyList, _Real, 1}}, 
      Table[{coef*EnergyList[[i]], uzmin[coef*EnergyList[[i]]], 
        uzmax[coef*EnergyList[[i]]], 
        If[uzmin[coef*EnergyList[[i]]] > 0.9999999, zmin, 1]}, {i, 1, 
        Length[EnergyList], 1}], CompilationTarget -> "C", 
      RuntimeOptions -> "Speed"] /. DownValues@uzmin /. 
   DownValues@uzmax // ReleaseHold

The latter is needed since my inverse CDF myInvCDF becomes infinite if uzmin becomes very close to 1 (numeric inaccuracy: Mathematica replaces say 1-Exp[-15] by 1). So, I regularize the infinity: if uzmin[coef*EnergyList[[i]]] > 0.9999999, then instead of calling this inverse CDF, I return zmin (if false, here for simplicity I return 1).

It turns out that this condition works very slow if ldecay becomes mostly comparable with zmin, or smaller:

coefval1 = 10000;
TestIf[coefval1, EnergyList]; // AbsoluteTiming
coefval1 = 10;
TestIf[coefval1, EnergyList]; // AbsoluteTiming

{0.114952, Null}

{28.6273, Null}

If I remove If, the table may be computed very fast.

Could you please tell me how to speed up this comparison, or regularize the Mathematica's inaccuracy when substituting values of u being very close to 1 in myInvCDF in a more efficient way?

$\endgroup$

2 Answers 2

2
$\begingroup$

You forgot to inline the zmin.

TestIf = Hold@
       Compile[{coef, {EnergyList, _Real, 1}}, 
        Table[{coef*EnergyList[[i]], uzmin[coef*EnergyList[[i]]], 
          uzmax[coef*EnergyList[[i]]], 
          If[uzmin[coef*EnergyList[[i]]] > 0.9999999, zmin, 1]}, {i, 1, 
          Length[EnergyList], 1}], CompilationTarget -> "C", 
        RuntimeOptions -> "Speed"] /. DownValues@uzmin /. DownValues@uzmax /. 
    (* See here *)OwnValues@zmin // ReleaseHold;

coefval1 = 10;
TestIf[coefval1, EnergyList]; // AbsoluteTiming
(* {0.0730369, Null} *)
$\endgroup$
2
$\begingroup$

If one avoids repetition and explicit iteration, then one get fast code without compiling:

TestIfNew[coef_,EnergyList_] := With[{X=coef*EnergyList},
  With[{Y=uzmin[X]},
    {X,Y,uzmax[X],N[1]+UnitStep[Y-0.9999999]*N[zmin-1]}
  ]]//Transpose;

Timing:

coefval1=10;
TestIfNew[coefval1,EnergyList]; // RepeatedTiming
(* 0.04 seconds *)
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.