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
Post a Comment