181
$\begingroup$

Is there somewhere a list on the functions that Compile can compile, or the cases in which a particular function can be compiled that I haven't found? I'd be glad even with a list of some of them which surprisingly aren't compilable, and how to do without them.

I am not happy every time I have to rewrite or redesign code because it seems to make external calls for functions I didn't expect. I'd like to know how you handle all that, what you keep in mind.

$\endgroup$

8 Answers 8

189
$\begingroup$

Yes, but this only exists in version 8 onwards and is undocumented:

Compile`CompilerFunctions[] // Sort

giving, for reference:

{Abs, AddTo, And, Append, AppendTo, Apply, ArcCos, ArcCosh, ArcCot, ArcCoth, ArcCsc,
 ArcCsch, ArcSec, ArcSech, ArcSin, ArcSinh, ArcTan, ArcTanh, Arg, Array, ArrayDepth,
 Internal`Bag, Internal`BagPart, BitAnd, BitNot, BitOr, BitXor, Block, BlockRandom, Boole,
 Break, Cases, Catch, Ceiling, Chop, Internal`CompileError, System`Private`CompileSymbol,
 Complement, ComposeList, CompoundExpression, Conjugate, ConjugateTranspose, Continue,
 Cos, Cosh, Cot, Coth, Count, Csc, Csch, Decrement, Delete, DeleteCases, Dimensions,
 Divide, DivideBy, Do, Dot, Drop, Equal, Erf, Erfc, EvenQ, Exp, Fibonacci, First,
 FixedPoint, FixedPointList, Flatten, NDSolve`FEM`FlattenAll, Floor, Fold, FoldList, For,
 FractionalPart, FreeQ, Compile`GetElement, Goto, Greater, GreaterEqual, Gudermannian,
 Haversine, If, Im, Implies, Increment, Inequality, Compile`InnerDo, Insert,
 IntegerDigits, IntegerPart, Intersection, InverseGudermannian, InverseHaversine,
 Compile`IteratorCount, Join, Label, Last, Length, Less, LessEqual, List, Log, Log10,
 Log2, LucasL, Map, MapAll, MapAt, MapIndexed, MapThread, NDSolve`FEM`MapThreadDot,
 MatrixQ, Max, MemberQ, Min, Minus, Mod, Compile`Mod1, Module, Most, N, Negative, Nest,
 NestList, NonNegative, Not, OddQ, Or, OrderedQ, Out, Outer, Part, Partition, Piecewise,
 Plus, Position, Positive, Power, PreDecrement, PreIncrement, Prepend, PrependTo, Product,
 Quotient, Random, RandomChoice, RandomComplex, RandomInteger, RandomReal, RandomSample,
 RandomVariate, Range, Re, ReplacePart, Rest, Return, Reverse, RotateLeft, RotateRight,
 Round, RuleCondition, SameQ, Scan, Sec, Sech, SeedRandom, Select, Set, SetDelayed,
 Compile`SetIterate, Sign, Sin, Sinc, Sinh, Sort, Sqrt, Internal`Square, Internal`StuffBag,
 Subtract, SubtractFrom, Sum, Switch, Table, Take, Tan, Tanh, TensorRank, Throw, Times,
 TimesBy, Tr, Transpose, Unequal, Union, Unitize, UnitStep, UnsameQ, VectorQ, Which,
 While, With, Xor}

As of Mathematica 10.0.2, there are also the following functions:

{Gamma, Indexed, LogGamma, LogisticSigmoid, Internal`ReciprocalSqrt}

As of Mathematica 11, there are also the following functions:

{Internal`Expm1, Internal`Log1p, Ramp}

As of Mathematica 11.2, there are also the following functions:

{RealAbs, RealSign}

About Tr:

Please note that Tr appears in this list, but cannot actually be compiled without a call to MainEvaluate[]. It is unclear if this is deliberate or a bug.


Edit: additional functions

I have just discovered the symbol Internal`CompileValues, which provides various definitions and function calls needed to compile further functions not in the list above. Using the following code,

Internal`CompileValues[]; (* to trigger auto-load *)
ClearAttributes[Internal`CompileValues, ReadProtected];
syms = DownValues[Internal`CompileValues] /. 
    HoldPattern[Verbatim[HoldPattern][Internal`CompileValues[sym_]] :> _] :>
        sym;
Complement[syms, Compile`CompilerFunctions[]]

we get some more compilable functions as follows:

{Accumulate, ConstantArray, Cross, Depth, Det, DiagonalMatrix,
Differences, NDSolve`FEM`FEMDot, NDSolve`FEM`FEMHold,
NDSolve`FEM`FEMInverse, NDSolve`FEM`FEMPart, NDSolve`FEM`FEMTDot,
NDSolve`FEM`FEMTotalTimes, NDSolve`FEM`FEMZeroMatrix, FromDigits,
Identity, IdentityMatrix, Inverse, LinearSolve, Mean, Median, Nand,
NestWhile, NestWhileList, Nor, Norm, Ordering, PadLeft, PadRight,
Permutations, Ratios, Signature, SquareWave, StandardDeviation,
Tally, Total, TrueQ, Variance}

Looking at the definition of Internal`CompileValues[sym] for sym in the list above will provide some additional information about how these functions are compiled. This can range from type information (for e.g. Inverse), through to an implementation in terms of lower-level functions (e.g. NestWhileList). One can presumably also make one's own implementations of non-compilable functions using this mechanism, giving Compile the ability to compile a wider range of functions than it usually would be able to.

As of Mathematica 10.3, there are also the following functions:

{DeleteDuplicates, Region`Mesh`SmallMatrixRank,
 Region`Mesh`SmallQRSolve, Region`Mesh`SmallSingularValues,
 Region`Mesh`SmallSingularValueSystem, Region`Mesh`SmallSVDSolve,
 NDSolve`SwitchingVariable}

As of Mathematica 11, there are also the following functions:

{NearestFunction, RegionDistanceFunction, RegionMemberFunction, RegionNearestFunction}

Edit 2: the meaning of the second list

In response to a recent question, I want to be clear that the presence of a function in the second list given above does not necessarily mean it can be compiled into a form free of MainEvaluate calls. If a top-level function is already highly optimized (as e.g. LinearSolve is), the purpose of Internal`CompileValues[func] may be solely to provide type information on the return value, assuming that this can be inferred from the types of the arguments or some other salient information. This mechanism allows more complex functions that call these highly-optimized top-level functions to be compiled more completely since there is no longer any question of what the return type may be and so further unnecessary MainEvaluate calls may be avoided. It does not imply that the use of MainEvaluate is unnecessary to call the function itself.

$\endgroup$
22
  • 7
    $\begingroup$ Interesting, Total[] isn't compilable, but Plus and Apply are... $\endgroup$ Feb 1, 2012 at 4:33
  • 11
    $\begingroup$ It looks like you've found a counterexample. Indeed, MemberQ[Compile`CompilerFunctions[], Total] == False, but in practice Total gets compiled down to a call to the internal kernel function TotalAll. Why Total is not included in the list, I have no idea. $\endgroup$ Feb 1, 2012 at 4:45
  • 1
    $\begingroup$ @MikeHoneychurch, Ted Ersek posted the old list (version 3/4 vintage) on his website. See here. There have been quite a few changes since then, though. $\endgroup$ Feb 1, 2012 at 23:47
  • 8
    $\begingroup$ To whoever downvoted this: would you be so kind as to let me know what your concerns were and perhaps suggest how I could improve my answers in future? Many thanks. $\endgroup$ Aug 18, 2012 at 23:10
  • 1
    $\begingroup$ @telefunkenvf14 I think there is not much more to be said about System`Utilities`HashTable* beyond what I already wrote here. That object definitely isn't related to the Internal`Bag; they are totally different data structures (also, the HashTable cannot be used in compiled code). Hash tables and linked lists are well-known data structures in computer science, so you will find a lot of information already out there. Any more specific questions... just ask! $\endgroup$ Dec 9, 2012 at 6:30
61
$\begingroup$

In addition to Oleks list, there is of course a way to study what happens under the hood.

f = Compile[{{x, _Integer, 1}},
   Accumulate[x]
   ];
<< CompiledFunctionTools`
CompilePrint[f]

(*

        1 argument
        1 Integer register
        2 Tensor registers
        Underflow checking off
        Overflow checking off
        Integer overflow checking on
        RuntimeAttributes -> {}

        T(I1)0 = A1
        I0 = 4
        Result = T(I1)1

1   T(I1)1 = Accumulate[ T(I1)0, I0]]
2   Return
*)

Here you see, that Accumulate can be compiled down, the question is where does this come from? Since version 8 you can compile to C-code, so let's do this and check what happens

<< CCodeGenerator`
CCodeGenerate[f, "fun", "tmp.c"];
FilePrint["tmp.c"]

I won't copy the whole output, but inspecting the code shows you

FP0 = funStructCompile->getFunctionCallPointer("Accumulate");

So the library extracts a function pointer from another (the WolframRTL) library. This library is distributed with Mathematica and you should have it in your SystemFiles/Libraries/$SystemID directory. You can now study this library by using tools like nm (on *nix systems) showing you the exported symbols. This is a very long list of course and maybe a bit cryptic to those not used to C-programming, but it should be readable. Here a short snip

00000000003327d0 T _MTensor_outerList
0000000000336e10 T _MTensor_outerPlus
00000000003352a0 T _MTensor_outerTimes
000000000032d1f0 t _MTensor_pTranspose
00000000003291b0 T _MTensor_pad
000000000032a580 T _MTensor_position
0000000000326140 T _MTensor_reallocateAndCopyData
0000000000325750 t _MTensor_releaseData
00000000003263f0 T _MTensor_resetDimensions
0000000000328f90 T _MTensor_reverse
000000000032cc80 T _MTensor_rotate

The question is, can you get more insight (if wanted) when inspecting this together with the list provided by Olek? I think it depends, sometimes yes, sometimes no. In my opinion, it is always nice to have a clue what's going on in the deep.

One example from the Compile`CompilerFunctions[] list is the function Outer. As you can see in the output above, this function does not directly exist, but is split into three forms, outerList, outerPlus and outerTimes. Regarding this, it seems Outer cannot be compiled in every form. Let's test this

f = Compile[{{x, _Integer, 1}},
   Outer[List, x, x]
   ];
CompilePrint[f]

(* 
  1 T(I3)1 = OuterList[ T(I1)0, T(I1)0, I0, I0]]
)*

This works as expected and we see the function OuterList is used. You can if you want to inspect the c-code too. Let me skip this here and try the same function with a Divide as head

f = Compile[{{x, _Integer, 1}},
   Outer[Divide, x, x]
   ];
(*
Compile::cpapot: Compilation of Outer[Divide,x,x] is not supported 
for the function argument Complex. The only function arguments supported 
are Times, Plus, or List. Evaluation will use the uncompiled function. >>
*)

If the error message would not point out directly, that Outer is only possible with the 3 heads, one could argue, that Divide is a bad function anyway since integers are not closed under this operation. You can easily try it with Complex and get the same message.

To summarize: Usually, you don't need any list of supported functions, because the rule of thumb is, that compile will not work with already optimized, complicated Mathematica-methods. This includes NIntegrate, FindRoot or NMinimize. Nevertheless, Compile can easily be used to make those function-calls really fast. What you have to do is to compile your target function, because the most time with stuff like NIntegrate is spent, evaluating the integrand. The same is true for FindRoot, NMinimize and many more methods.

Another good indicator to guess whether or not a function is supported is to look at the kinds of functions of Oleks list. There are exceptions to this rule but basically the list of supported functions can be divided into two classes. Simple numerical functions like Sin or Xor and functions which help you to work with tensors. I don't remember how often I wished I simply had Tally, Map or Fold in C. Even the addition or multiplication of tensors must be done manually.

Therefore, if a Mathematica-function implements a complicated method, when it's not a mathematical function or when it does not help you working with tensors/lists, it is most probably not supported by Compile.

$\endgroup$
2
  • $\begingroup$ +1. Regarding your Outer example - which is good in this context - I just want to add that it is rather special. There are good reasons why it can only be compiled for the heads you named. In this answer: stackoverflow.com/questions/4973424/…, I show how to make compilation of Outer more flexible, and also discuss the reasons why compiling Outer is tricky. $\endgroup$ Feb 1, 2012 at 13:40
  • $\begingroup$ Yes, you are right. I scrolled through the list and was trying to find an really short example. It was the first catch my eye. $\endgroup$
    – halirutan
    Feb 1, 2012 at 13:53
45
$\begingroup$

Warning: The SetSystemOptions method to detect failed compilation, described below, is not 100% reliable. Please see the comments (e.g. trC = Compile[{{a, _Integer, 2}}, Tr[a]] won't warn).


I assume you need the list of compilable functions to make sure that all of your code will be properly compiled, and it won't take any speed penalties (that why I was looking for this information before). People have shown you how to print the compiled code and check that there are no calls to MainEvaluate in it. There is an alternative and simpler way of working:

SetSystemOptions["CompileOptions" -> "CompileReportExternal" -> True]

After setting this, Compile will warn about uncompilable things:

In[4]:= cf1 = Compile[{x}, Total[x]]

During evaluation of In[4]:= Compile::extscalar: 
  Total[x] cannot be compiled and will be evaluated externally. 
  The result is assumed to be of type Real. >>

Out[4]= CompiledFunction[{x},Total[x],-CompiledCode-]

In[5]:= cf2 = Compile[{{x,_Integer,1}}, Total[x]]

Out[5]= CompiledFunction[{x},Total[x],-CompiledCode-]

This is better than just thinking about which function is compilable and which isn't because as you can see, whether something can be compiled also depends on context (Total in this example.)


For completeness, let me show that CompilePrint[cf1] gives

        R0 = A1
        Result = R1

1   R1 = MainEvaluate[ Hold[Total][ R0]]
2   Return

while CompilePrint[cf2] gives

        T(I1)0 = A1
        I0 = 4
        Result = I1

1   I1 = TotalAll[ T(I1)0, I0]]
2   Return
$\endgroup$
6
  • $\begingroup$ Could you please comment on why trC = Compile[{{a, _Integer, 2}}, Tr[a]] calls MainEvaluate and why your method doesn't trigger an error message (after setting the appropriate system option)? $\endgroup$ Aug 23, 2013 at 15:02
  • $\begingroup$ @IstvánZachar Unfortunately, I cannot. It seems the method is not reliable. I should update the answer. $\endgroup$
    – Szabolcs
    Aug 23, 2013 at 15:10
  • $\begingroup$ @IstvánZachar For lack of time, I'll just put a warning at the top of the answer now. If you have other suggestions, please let me know. $\endgroup$
    – Szabolcs
    Aug 23, 2013 at 15:13
  • $\begingroup$ @Oleksandr, Szabolcs I think the problem of not throwing an error is covered here, though why Tr cannot be compiled primarily without ME eludes me. $\endgroup$ Aug 27, 2013 at 15:35
  • $\begingroup$ @IstvánZachar Fortunately it's relatively easy to create a compiled Tr alternative. $\endgroup$
    – Szabolcs
    Aug 27, 2013 at 17:45
35
$\begingroup$

A bit late to the discussion but I had the chance to ask Wolfram's North America sales manager about this. I had forwarded him the list of compilable functions "Compile`CompilerFunctions[]".

He asked one of his engineers about this. They came back with some information that I thought would add to this question.

I don't think that is the correct interpretation of the list Sort[Compile`CompilerFunctions[]]. This list consists of those system functions that are pre-compiled. But any numerical function can be compiled to C and basically any user defined function that does not use a purely symbolic function can be compiled to C, for eg:

In[1]:= square=Compile[{x},x^2]  
Out[1]= CompiledFunction[{x},x^2,-CompiledCode-]   

In[2]:= square /@ {1.3,3.5,6.7} 
Out[2]= {1.69,12.25,44.89}   

And even for symbolic functions, you can get them to evaluate before compilation using CompileEvaluate[..]

Mathematica's compiler is more powerful than other programs because it also allows for checking of types in the compiled expression.

....

MORE FROM WOLFRAM

As I mentioned in a comment to Oleksandr, I went back to the sales manager for a clarification. He forwarded the issue to a senior developer. The reply just came back and follows (with some minor formatting):

It (compiling) is not as simple as just a list of functions.

For a number of functions, some uses of the function are supported and others are not.

Thus, the list of functions is a starting point, but does not give the whole story. The following function is a simple test that tells you if a function can be compiled to stand alone C or not:

Needs["CCodeGenerator`"];

SetAttributes[CCompileQ, HoldAll];
CCompileQ[args__] := Module[{cf = Compile[args]},
   Check[CCodeStringGenerate[cf, "test",
     "CodeTarget" -> "WolframRTL"]; True, False, CCodeGenerate::wmreq]]

examples:

In[37]:= CCompileQ[{x}, x^2]
Out[37]= True

In[38]:= CCompileQ[{x}, f[x]]
Out[38]= False

During evaluation of In[38]:= CCodeGenerate::wmreq: The expression f requires Mathematica to be evaluated. The function will be generated but can be expected to fail with a nonzero error code when executed.

Note that the message will give one instance of something in the expression that could not be compiled to C.

$\endgroup$
8
  • $\begingroup$ Interesting response, but this engineer seems to be wrong. Are you sure the sales manager didn't write this (especially the last sentence) while claiming that an engineer has passed on the information? ;) Some of the listed functions are pre-compiled in the sense that there's a corresponding function in the Mathematica RTL that can be called directly from the VM, but there are counterexamples (e.g. Total which is missing from the first list even though it compiles down to a call to the internal function TotalAll) that show that this isn't a sufficient condition for their inclusion. $\endgroup$ Feb 7, 2013 at 0:16
  • 1
    $\begingroup$ Also, many of the special functions, though numerical, cannot be compiled. MathieuCPrime is one that comes immediately to mind--and it even has the NumericFunction attribute. $\endgroup$ Feb 7, 2013 at 0:25
  • $\begingroup$ @OleksandrR. -- I can go back to the sales manager with your thoughts. I'll also ask if the engineer can visit this question. $\endgroup$
    – Jagra
    Feb 7, 2013 at 2:44
  • $\begingroup$ that would be very useful, because although I don't really have any doubts about the applicability of the list as an answer to this question, it's not clear to me how it's used internally by the compiler and whether every compilable function is given by either Compile`CompilerFunctions[] or as a downvalue of Internal`CompileValues. Information on how to modify the latter to enable the compilation of additional functions would be particularly helpful. I'm not sure who wrote the compiler but I think either Mark Sofroniou or Rob Knapp will probably be good people to ask. $\endgroup$ Feb 7, 2013 at 14:38
  • 4
    $\begingroup$ So, we've progressed from "the list isn't meaningful since anything can be compiled" to "only some functions are compilable, but the list is a partial description". That's better, although I had hoped for some information to be provided that we didn't know already. Anyway, thanks very much for taking the time to inquire! (BTW, I'm not sure why they focus so much on compilation to C. Not all compilable functions can be translated to C, e.g. Compile[{}, Label[1]; Goto[1]], which fails due to a bug in the CCodeGenerator` package.) $\endgroup$ Mar 5, 2013 at 14:02
28
$\begingroup$

I believe there is such a list available but I can't remember the command off-hand. In the meantime, you can always load CompiledFunctionTools via.

<<CompiledFunctionTools`

And then use CompilePrint on a compiled function to see if MainEvaluate is present in the pseudocode. MainEvaluate tells us that something is going through the evaluator and wasn't compilable.

f=Compile[{{x,_Real,1}},
  Print[x];
  x
];

In[61]:= CompilePrint[f]

Out[61]= 
        1 argument
        1 Tensor register
        Underflow checking off
        Overflow checking off
        Integer overflow checking on
        RuntimeAttributes -> {}

        T(R1)0 = A1
        Result = T(R1)0

1   V17 = MainEvaluate[ Function[{x}, Print[x]][ T(R1)0]]
2   Return

Notice the MainEvaluate in the call to Print. This means Print isn't compilable.

$\endgroup$
19
$\begingroup$

The following additional functions are compilable in Mathematica 9.

{Gamma, LogGamma, Internal`ReciprocalSqrt}
$\endgroup$
12
$\begingroup$

The following are compilable in M10 but aren't in previous versions:

{Indexed, LogisticSigmoid}
$\endgroup$
1
  • 2
    $\begingroup$ Nice, I didn't know about Indexed and it seems like a long due function $\endgroup$
    – Rojo
    Oct 9, 2014 at 3:33
12
$\begingroup$

Span can be compiled:

<< CompiledFunctionTools`

cf = Compile[{{lst, _Real, 1}}, lst[[;; 2]]];
CompilePrint@cf

enter image description here

Pictured by Simon Wood's shadow.


All used as an argument of Part can be compiled:

cf2 = Compile[{{lst, _Real, 2}}, lst[[All, ;; 2]]];
CompilePrint@cf2

enter image description here

while All used as an argument of Span, which is the FullForm of (* a number* ) ;; , can't be compiled:

2 ;; // FullForm
Span[2, All]
cf3 = Compile[{{lst, _Real, 1}}, lst[[2 ;;]]];

Compile::cpintlt: 2;;All at position 2 of lst[[2;;All]] should be either a nonzero integer or a vector of nonzero integers; evaluation will use the uncompiled function. >>


Pure function i.e. Function formed by above-mentioned compilable functions can usually be compiled:

cf4 = Compile[{x}, Sin[#] &@x];
cf4 // CompilePrint
……
1 R1 = R0
2 R2 = Sin[ R1]
……

Ordering, which is in the 2nd list of Oleksandr R.'s answer, can be compiled:

cf5 = Compile[{{x, _Real, 1}}, Ordering@x];
cf5 // CompilePrint
……
1 T(I1)1 = Ordering[ T(R1)0]]
……

Blank can be compiled when it's the last pattern of Switch:

cf6 = Compile[{i}, Switch[i, 1., 100, _, 300]]    
cf6 // CompilePrint
……
1 B0 = R0 None R1 (tol R1)
2 if[ !B0] goto 5
3 I2 = I0
4 goto 6
5 I2 = I1
6 Return

You may check this post for more information.

$\endgroup$
2
  • $\begingroup$ Surprisingly, lst[[2 ;; -1]] can be compiled while lst[[2;;]] can't. If one changes All in cf2 to be ;;, the error will show up again. This can be also got around by changing ;; to 1;;-1 if one sticks to the shorthand notation of Span. $\endgroup$
    – luyuwuli
    Nov 21, 2015 at 9:20
  • $\begingroup$ @luyuwuli That's all because of All, the FullForm of ;; is Span[1, All] actually. $\endgroup$
    – xzczd
    Nov 21, 2015 at 10:07

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.