# Is it possible to create a compiled function with some symbolic arguments?

I am trying to create a compiled function that takes in several arguments. However, some of the arguments contain symbolic entries and thus I get the following error message when executing the cell containing the compiled function:

Compile::cplist: b should be a tensor of type Integer, Real, or Complex; evaluation will use the uncompiled function.

Compile::cret: The type of return values in [...] are different. Evaluation will use the uncompiled function.

and this error message when trying to call the function:

CompiledFunction::cfta: "Argument {<<1>>} at position 2 should be a rank 2 tensor of !(\"machine-size real number\")s. "

Here is the compiled function I am trying to create:

calcK=Compile[{{nnpe,_Integer,0},b,{d,_Real,2},j,{t,_Real,0}},
Module[
k=Array[0&,{nnpe*2,nnpe*2}];
p=1/Sqrt[3];
pCoords={{-p,-p},{p,-p},{p,p},{-p,p}};
w={1,1,1,1};
k+=Transpose[b].d.b*Det[j]*w[[i]]/.{xi->pCoords[[i,1]],eta->pCoords[[i,2]]};
];
Return[t*k]];
];


Here is some sample input data (I apologize in advance for the large lists):

nnpe=4;
t=.25;
d={{1.0989*10^9, 3.2967*10^8, 0}, {3.2967*10^8, 1.0989*10^9, 0}, {0, 0,3.84615*10^8}};
b={{((-(1/4) + eta/4)*(0.25*(1/4 - xi/4) + 0.25*(1/4 + xi/4)))/(0. - 0.625*eta) +
((-0.25*(-(1/4) - eta/4) - 0.25*(1/4 + eta/4))*(-(1/4) + xi/4))/(0. - 0.625*eta),
0, ((1/4 - eta/4)*(0.25*(1/4 - xi/4) + 0.25*(1/4 + xi/4)))/(0. - 0.625*eta) +
((-0.25*(-(1/4) - eta/4) - 0.25*(1/4 + eta/4))*(-(1/4) - xi/4))/(0. - 0.625*eta),
0, ((1/4 + eta/4)*(0.25*(1/4 - xi/4) + 0.25*(1/4 + xi/4)))/(0. - 0.625*eta) +
((-0.25*(-(1/4) - eta/4) - 0.25*(1/4 + eta/4))*(1/4 + xi/4))/(0. - 0.625*eta), 0,
((-(1/4) - eta/4)*(0.25*(1/4 - xi/4) + 0.25*(1/4 + xi/4)))/(0. - 0.625*eta) +
((-0.25*(-(1/4) - eta/4) - 0.25*(1/4 + eta/4))*(1/4 - xi/4))/(0. - 0.625*eta),
0}, {0, ((-(1/4) + eta/4)*(-10.*(-(1/4) - xi/4) - 10.*(1/4 - xi/4)))/
(0. - 0.625*eta) + ((10.*(-(1/4) - eta/4) + 10.*(1/4 - eta/4))*(-(1/4) + xi/4))/
(0. - 0.625*eta), 0, ((1/4 - eta/4)*(-10.*(-(1/4) - xi/4) - 10.*(1/4 - xi/4)))/
(0. - 0.625*eta) + ((10.*(-(1/4) - eta/4) + 10.*(1/4 - eta/4))*(-(1/4) - xi/4))/
(0. - 0.625*eta), 0, ((1/4 + eta/4)*(-10.*(-(1/4) - xi/4) - 10.*(1/4 - xi/4)))/
(0. - 0.625*eta) + ((10.*(-(1/4) - eta/4) + 10.*(1/4 - eta/4))*(1/4 + xi/4))/
(0. - 0.625*eta), 0,
((-(1/4) - eta/4)*(-10.*(-(1/4) - xi/4) - 10.*(1/4 - xi/4)))/(0. - 0.625*eta) +
((10.*(-(1/4) - eta/4) + 10.*(1/4 - eta/4))*(1/4 - xi/4))/(0. - 0.625*eta)},
{((-(1/4) + eta/4)*(-10.*(-(1/4) - xi/4) - 10.*(1/4 - xi/4)))/(0. - 0.625*eta) +
((10.*(-(1/4) - eta/4) + 10.*(1/4 - eta/4))*(-(1/4) + xi/4))/(0. - 0.625*eta),
((-(1/4) + eta/4)*(0.25*(1/4 - xi/4) + 0.25*(1/4 + xi/4)))/(0. - 0.625*eta) +
((-0.25*(-(1/4) - eta/4) - 0.25*(1/4 + eta/4))*(-(1/4) + xi/4))/(0. - 0.625*eta),
((1/4 - eta/4)*(-10.*(-(1/4) - xi/4) - 10.*(1/4 - xi/4)))/(0. - 0.625*eta) +
((10.*(-(1/4) - eta/4) + 10.*(1/4 - eta/4))*(-(1/4) - xi/4))/(0. - 0.625*eta),
((1/4 - eta/4)*(0.25*(1/4 - xi/4) + 0.25*(1/4 + xi/4)))/(0. - 0.625*eta) +
((-0.25*(-(1/4) - eta/4) - 0.25*(1/4 + eta/4))*(-(1/4) - xi/4))/(0. - 0.625*eta),
((1/4 + eta/4)*(-10.*(-(1/4) - xi/4) - 10.*(1/4 - xi/4)))/(0. - 0.625*eta) +
((10.*(-(1/4) - eta/4) + 10.*(1/4 - eta/4))*(1/4 + xi/4))/(0. - 0.625*eta),
((1/4 + eta/4)*(0.25*(1/4 - xi/4) + 0.25*(1/4 + xi/4)))/(0. - 0.625*eta) +
((-0.25*(-(1/4) - eta/4) - 0.25*(1/4 + eta/4))*(1/4 + xi/4))/(0. - 0.625*eta),
((-(1/4) - eta/4)*(-10.*(-(1/4) - xi/4) - 10.*(1/4 - xi/4)))/(0. - 0.625*eta) +
((10.*(-(1/4) - eta/4) + 10.*(1/4 - eta/4))*(1/4 - xi/4))/(0. - 0.625*eta),
((-(1/4) - eta/4)*(0.25*(1/4 - xi/4) + 0.25*(1/4 + xi/4)))/(0. - 0.625*eta) +
((-0.25*(-(1/4) - eta/4) - 0.25*(1/4 + eta/4))*(1/4 - xi/4))/(0. - 0.625*eta)}};
j={{10.*(-(1/4) - eta/4) + 10.*(1/4 - eta/4), 0.25*(-(1/4) - eta/4) +
0.25*(1/4 + eta/4)}, {10.*(-(1/4) - xi/4) + 10.*(1/4 - xi/4),
0.25*(1/4 - xi/4) + 0.25*(1/4 + xi/4)}};


And finally, I call the compiled function with:

calcK[nnpe, b, d, j, t]


which produces the aforementioned error message. I'm thinking it has something to do with the fact that the "b" and "j" matrices contain "xi" and "eta". Thus, I cannot define the type in the compiled function definition.

-
The simple answer here is that compiled functions cannot work with symbols. They always must have numeric arguments (tensors or scalars, but not ragged arrays). The function will need to be rewritten to do the substitution outside of the compiled part of pass the result to a smaller compiled function. – Szabolcs Apr 3 '13 at 21:19
This question is interesting and could prevent new users of Compile[] to ask to much to Compile[]. Nevertheless it would be good to have a more concise code (no more than the necessary code that reveals the problem) – andre Apr 3 '13 at 21:42

The complexity of the code makes it hard to check things, but there are some simplifications of the example in the question that can be used to get around having to pass symbols to your function. This can speed up the calculation as well.

First, these seem to be constants inside the function you're compiling:

p = 1/Sqrt[3];
pCoords = {{-p, -p}, {p, -p}, {p, p}, {-p, p}};
w = {1, 1, 1, 1};


If so, they don't really have to be in the function.

Next, we can transform b and j into lists of matrices. It would be better if b and j could be constructed in this form from the beginning. Edit: Here's a straightforward way to set them up:

blist = Table[b /. {xi -> pCoords[[i, 1]], eta -> pCoords[[i, 2]]}, {i, Length@pCoords}];
jlist = Table[j /. {xi -> pCoords[[i, 1]], eta -> pCoords[[i, 2]]}, {i,  Length@pCoords}];


Then we can simplify your function like this:

calcK = Compile[{{blist, _Real, 3}, {d, _Real, 2}, {jlist, _Real, 3}, {t, _Real, 0}},
t*Total[MapThread[Dot, {Transpose /@ blist, d.# & /@ blist}] Det /@ jlist]];

calcK[blist, d, jlist, t] (* with d and t as in the question *)


The argument nnpe seems unnecessary since it is embedded in the dimensions of the other arguments. The vector w just multiplies each component by 1; perhaps you intend it as a place holder. If w is nontrivial, then one could pass it as an argument to the function:

calcK = Compile[{{blist, _Real, 3}, {d, _Real, 2}, {jlist, _Real, 3}, {t, _Real, 0}, {w, _Real, 1}},
t * w.(MapThread[Dot, {Transpose /@ blist, d.# & /@ blist}] Det /@ jlist)];

calcK[blist, d, jlist, t, {1,1,1,1}]


It agrees with an uncompiled version of your function (to ~14 digits).

Notes: 1. Det is not compilable, but it's only called once. One could pass the determinants of the matrices in jlist to the function calcK instead of jlist`. 2. Mathematica can deal with lists (vectors, matrices, etc.) efficiently, both in terms of writing code and in terms of computational speed.

-
Thanks for the answer! I ended up doing something extremely similar. – Matthew Apr 4 '13 at 2:31