Threading a compiled function over multiple arguments of different lengths

I am interested in threading a compiled function over multiple argument lists of different lengths.

I will explain this in two steps:

Suppose f is some function. Then one can define another function,

ppost[arg_, context_] := Apply[f[arg, ##] &, context]


which can be used as

In[2]:= ppost[a, {b, c}]

Out[2]= f[a, b, c]


or in a more complex scenario let,

In[3]:= argList = {{a1, b1}, {a2, b2, c2}};

In[4]:= mList = {{m1, n1}, {m2, n2, o2}};

In[5]:= contextList = {mList, y, z}

Out[5]= {{{m1, n1}, {m2, n2, o2}}, y, z}


We can thread the function over the argList and the contextList as follows:

In[6]:= Thread[ppost[argList, contextList]]

Out[6]= {f[{a1, b1}, {m1, n1}, y, z],
f[{a2, b2, c2}, {m2, n2, o2}, y, z]}


The above works, as needed.

Question: I need to do the same when f is a compiled function. As an example,

fc = Compile[{{arg, _Real, 1}, {c1, _Real, 1}, {c2, _Real}, {c3, _Real}},
c3*arg + c2*c1
]

ppost1[arg_, context_] := Apply[fc[arg, ##] &, context]

In[9]:= nargList = {{1, 2}, {3, 4, 5}} // N;

In[10]:= nmList = {{6, 7}, {8, 9, 10}} // N;

In[11]:= ncontextList = {nmList, 50, 55} // N

Out[11]= {{{6., 7.}, {8., 9., 10.}}, 50., 55.}

In[12]:= ppost1[nargList[[1]], {nmList[[1]], 50, 55}]

Out[12]= {355., 460.}


So far, so good, but then the following does not work properly, and uses the uncompiled version.

In[14]:= Thread[ppost1[nargList, ncontextList]]

During evaluation of
In[14]:= CompiledFunction::cfta: Argument {{1.,2.},{3.,4.,5.}} at position 1
should be a rank 1 tensor of machine-size real numbers. >>

During evaluation of
In[14]:= Thread::tdlen: Objects of unequal length in
{{355.,460.},{565.,670.,775.}} cannot be combined. >>

Out[14]= {{355., 460.}, {565., 670., 775.}}


So, how can I thread the compiled function over the arguments nargList, ncontextList?

-

I think your problem is equivalent to trying to get something like this

l := Power[2, 2]

f /@ l


return f[2]^f[2] instead of 4

You can control argument evaluation in MMA, for it to be either evaluated or not evaluated, but not half evaluated. If you wanted that you would have to either redesign your variables, or use things like replacement rules of OwnValues/DownValuess

In your case, you are expecting ppost1 to evaluate to fc, but fc not try to evaluate its arguments (until Thread had a chance to act)

If someone understood better than I did what your goal is, perhaps he can help you do it in some way

EDIT

Perhaps what you're looking for then can be achieved by masking the compiled function with a function that knows how to stay symbolically unevaluated

With[{cmp =
Compile[{{arg, _Real, 1}, {c1, _Real,
1}, {c2, _Real}, {c3, _Real}}, c3*arg + c2*c1]},
fc[arg : {___Real}, c1 : {___Real}, c2_Real, c3_Real] :=
cmp[arg, c1, c2, c3]
]

-
Rojo, please see my answer above, especially the last part. Do you think my understanding of the question is correct? –  Mr.Wizard Feb 7 '12 at 4:41
@Spartacus I think you probably got it. But perhaps he needs that ppost function for something. He'll probably tell us soon –  Rojo Feb 7 '12 at 4:52
please see this post, then try Map[f, step[l], {2}] –  Mr.Wizard Feb 7 '12 at 12:40
I need the ppost1 function as it is supposed to be used in another function that expects the signature of ppost1, i.e., an argument arg, and a context. –  asim Feb 7 '12 at 13:53
@asim, read my edit then –  Rojo Feb 7 '12 at 16:32

A possible solution it to use the Listable attribute of compile.

fc2 = Compile[{{arg, _Real, 1}, {c1, _Real,
1}, {c2, _Real}, {c3, _Real}}, c3*arg + c2*c1,
RuntimeAttributes -> Listable]


Then call like this:

fc2[nargList, nmList, {50, 50}, {55, 55}]


What you really want is:

(* fc2[nargList, nmList, 50, 55] *)


Unfortunately, this crashes my V8.0.4 Kernel, but it's fixed in the development version. Hope this helps.

-

Your syntax between the two functions is different. In terms of Thread I think you mean to use:

nargList = {{1, 2}, {3, 4, 5}} // N;

nmList = {{6, 7}, {8, 9, 10}} // N;

ncontextList = {nmList, 50, 55} // N;



{ppost1[{1., 2.}, {6., 7.}, 50., 55.], ppost1[{3., 4., 5.}, {8., 9., 10.}, 50., 55.]}

But there seems to me to be another problem: your function is applied before the threading takes place. I am finding it difficult to divine your ultimate intent but I suspect this is not working the way you think it is.

Reading the question a second time you appear to be confused regarding the way Thread works. Only elements that appear at the first level of func are distributed among lists. Further, any lists over which the function is threaded must be of the same length. Please consider these examples:

Thread[func[{1, 2, 3}, {4, 5, 6}, 7]]

{func[1, 4, 7], func[2, 5, 7], func[3, 6, 7]}

Thread[func[{1, 2, 3}, {4, 5, 6}, {7}]]


During evaluation of In[2]:= Thread::tdlen: Objects of unequal length in func[{1,2,3},{4,5,6},{7}] cannot be combined. >>

Thread[func[{1, 2, 3}, {4, 5, 6, 7}]]


During evaluation of In[3]:= Thread::tdlen: Objects of unequal length in func[{1,2,3},{4,5,6,7}] cannot be combined. >>

Thread[func[{1, 2, 3}, {{4, 5, 6}, 7}]]


During evaluation of In[4]:= Thread::tdlen: Objects of unequal length in func[{1,2,3},{{4,5,6},7}] cannot be combined. >>

Finally, I believe that this is probably what you are trying to do:

(EDIT: Since you need ppost1 then I should have written:)

Block[{fc},

{{355., 460.}, {565., 670., 775.}}

@asim please see the edit at the bottom of my post for an alternative to modifying fc`. I am sorry that I did not understand the heart of your question sooner. –  Mr.Wizard Feb 7 '12 at 22:05