Skip to main content

about Compile and return type


In the mathematica document "tutorial/CompilingWolframLanguageExpressions"


1


It says about how to set return type of a called function you used in Compile.


Clear[com]
com[i_] := Binomial[2 i, i]

test=Compile[{x, {i, _Integer}}, x^com[i], {{com[_], _Integer}}]

From the above example, we can see that because the com function evaluates to integer, so we set {{com[_], _Integer}} to let Compile know the return type of com.



But if you inspect it further


Needs["CompiledFunctionTools`"]
CompilePrint[test]

You can see there is MainEvaluate when calling function com.


So, I don't understand the meaning of this example in the document. If a compiled function has a MainEvaluate process, then I think compiling it is just nonsense, for it won't speed up things, right?


2


Then I came up with another question, it is also mentioned in the same document page. If we compile Sqrt as below


sqrtcom1 = Compile[{{x, _Real}}, Sqrt[x]]


we will run into problems if we evaluate sqrtom1[-1.], it will give errors like



CompiledFunction::cfn: Numerical error encountered at instruction 1; proceeding with uncompiled evaluation. >>



This is because the return type of Sqrt is assumed to be real by default. This can be see from


In[20]:= ToCompiledProcedure[sqrtcom1][[4]]

Out[20]= CompiledResult[Register[Real, 1]]

So, theoretically we could solve this by



sqrtcom2 = Compile[{{x, _Real}}, Sqrt[x], {{Sqrt[_], _Complex}}]

But this is not working!! ToCompiledProcedure[sqrtcom2][[4]] still gives CompiledResult[Register[Real, 1]] and sqrtcom2 still gives errors.


Why is it not working?



Answer



Let me counter Daniel Lichtblau's answer



This has zero to do with type inferencing.



by saying, the example in the tutorial you linked is all about type inference. It is not about compiling com to make it faster. It is about helping the compiler to deduce the correct type for the expression.



You have to understand one thing: Highlevel Mathematica language is untyped, which means that it is not known upfront whether Sqrt[x] is an integer, a real, a complex or whether it stays as a general expression. It all depends on the value that x has.


Compiled code is completely different, because all variables will have a type. Either explicitly given by you, or derived/assumed by the compiler.


Therefore, your first question is not about whether or not com inside the compile will be too slow. The question is, can the compiler derive the type of com and therefore assume a correct return type.


Since this example does work correctly even without the explicit type hint, let me give a different example. Here, realf is a function that returns a real number, when the input is an integer. In Mathematica 10.3, this leads to an error message:


realf[i_] := 1.5*i;
f1 = Compile[{{i, _Integer}}, realf[i]]

Calling f1[3] will give you a warning, saying that realf will be the reason that the uncompiled version of f1 is used. If you check the f1 with CompilePrint you will find the line


I1 = MainEvaluate[ Hold[realf][ I0]]


The important part is that I1 means integer register. So because Compile has no information about realf, it assumes this call will be of type integer which is wrong. If we change the definition to


f2 = Compile[{{i, _Integer}}, realf[i], {{realf[_], _Real}}]

and check the compiled code again, we see that now a real register is used for the result of realf.


R0 = MainEvaluate[ Hold[realf][ I0]]

Therfore, f2[3] will run without message since the types are consistent. Nevertheless, realf will be an external call that is evaluated by the kernel.


What Daniel's answer is showing you is, that in the specific example of com being defined as Binomial, you can expand the call and indeed compile all instructions to gain a lot of speed.


As for your second example,


Compile[{{x, _Real}}, Sqrt[x]]


there is one additional thing to note: You call Sqrt[x] where x is the input of type Real. Therefore, the compiler deduces that you want the square-root that works on reals. It seems, not even the type hint at the end of Compile prevents this, but there is a simpler solution:


sqrtHal = Compile[{{x, _Real}}, 
Module[{xx = 0. I},
xx = x;
Sqrt[xx]
]
]

Look what we did: we created another variable xx and by giving an initial complex value, we force the type-system to assume xx to be complex. The rest works like in most programming languages. When we assign xx=x a type-conversion takes place and xx is still complex where the imaginary part is zero and the real part is x. Furthermore, the correct complex Sqrt function is selected and therefore



sqrtHal[-1]
(* 0. + 1. I *)

works without problems.


As soon as you have understood the reasons behind this, you easily find another solution:


sqrtHal2 = Compile[{{x, _Real}},
Sqrt[x + 0. I]
]

Comments

Popular posts from this blog

plotting - Plot 4D data with color as 4th dimension

I have a list of 4D data (x position, y position, amplitude, wavelength). I want to plot x, y, and amplitude on a 3D plot and have the color of the points correspond to the wavelength. I have seen many examples using functions to define color but my wavelength cannot be expressed by an analytic function. Is there a simple way to do this? Answer Here a another possible way to visualize 4D data: data = Flatten[Table[{x, y, x^2 + y^2, Sin[x - y]}, {x, -Pi, Pi,Pi/10}, {y,-Pi,Pi, Pi/10}], 1]; You can use the function Point along with VertexColors . Now the points are places using the first three elements and the color is determined by the fourth. In this case I used Hue, but you can use whatever you prefer. Graphics3D[ Point[data[[All, 1 ;; 3]], VertexColors -> Hue /@ data[[All, 4]]], Axes -> True, BoxRatios -> {1, 1, 1/GoldenRatio}]

plotting - Filling between two spheres in SphericalPlot3D

Manipulate[ SphericalPlot3D[{1, 2 - n}, {θ, 0, Pi}, {ϕ, 0, 1.5 Pi}, Mesh -> None, PlotPoints -> 15, PlotRange -> {-2.2, 2.2}], {n, 0, 1}] I cant' seem to be able to make a filling between two spheres. I've already tried the obvious Filling -> {1 -> {2}} but Mathematica doesn't seem to like that option. Is there any easy way around this or ... Answer There is no built-in filling in SphericalPlot3D . One option is to use ParametricPlot3D to draw the surfaces between the two shells: Manipulate[ Show[SphericalPlot3D[{1, 2 - n}, {θ, 0, Pi}, {ϕ, 0, 1.5 Pi}, PlotPoints -> 15, PlotRange -> {-2.2, 2.2}], ParametricPlot3D[{ r {Sin[t] Cos[1.5 Pi], Sin[t] Sin[1.5 Pi], Cos[t]}, r {Sin[t] Cos[0 Pi], Sin[t] Sin[0 Pi], Cos[t]}}, {r, 1, 2 - n}, {t, 0, Pi}, PlotStyle -> Yellow, Mesh -> {2, 15}]], {n, 0, 1}]

plotting - Mathematica: 3D plot based on combined 2D graphs

I have several sigmoidal fits to 3 different datasets, with mean fit predictions plus the 95% confidence limits (not symmetrical around the mean) and the actual data. I would now like to show these different 2D plots projected in 3D as in but then using proper perspective. In the link here they give some solutions to combine the plots using isometric perspective, but I would like to use proper 3 point perspective. Any thoughts? Also any way to show the mean points per time point for each series plus or minus the standard error on the mean would be cool too, either using points+vertical bars, or using spheres plus tubes. Below are some test data and the fit function I am using. Note that I am working on a logit(proportion) scale and that the final vertical scale is Log10(percentage). (* some test data *) data = Table[Null, {i, 4}]; data[[1]] = {{1, -5.8}, {2, -5.4}, {3, -0.8}, {4, -0.2}, {5, 4.6}, {1, -6.4}, {2, -5.6}, {3, -0.7}, {4, 0.04}, {5, 1.0}, {1, -6.8}, {2, -4.7}, {3, -1....