# Using Outer with Compiled functions that accept more than 2 arguments

How does one use Outer with a compiled function that accepts 3 or more arguments. Alternatively, how does one create a compiled function with 3 or more arguments that can be used with Outer?

I am trying to use the following function:

minimumImagePD =  Compile[{{x, _Real, 1}, {y, _Real, 1}, {z, _Real}},
Total[( (x - y) - z * Round [ (x - y) / z]) ^ 2]]


This function compiles just fine but you can't use it with Outer. Trying the following doesn't work because Compile does not "see" the argument 'z':

minimumImagePD2[z_Real] =  Compile[{{x, _Real, 1}, {y, _Real, 1}},
Total[((x - y) - z * Round[ (x - y)/z] ) ^ 2]]

Outer[minimumImagePD2[3.2], {{2.1, 3.2, 4.3}}, {{3.2, 4.3, 2.8},
{1.1, 2.2, 3.3},   {3.4, 2.0, 6.5}}, 1].


Now, If I replace z in minimumImagePD with a number as follows:

minimumImagePD3 =  Compile[{{x, _Real, 1}, {y, _Real, 1}},
Total[((x - y) - 3.25026*Round[(x - y)/3.25026])^2]]


The function compiles and works fine as with the following example:

Outer[minimumImagePD3, {{2.3, 4.3, 6.5}}, {{2.1, 4.8, 7.3}, {2.2,
1.1, 4.3}, {2.1, 3.3, 4.7}}, 1] // Flatten


Which gives:

{0.93, 1.11557, 3.14325}


As noted by Oleksandr R. below, using set-delayed in minimumImagePD2 solves the problem.

-
The problem is not the number of arguments. It is that Total doesn't accept a sequence as argument. The solution : minimumImagePD = Compile[{{x, _Real, 1}, {y, _Real, 1}, {z, _Real}}, Total[List @ ((x - y) - z*Round[(x - y)/z])^2]] – andre Feb 7 '13 at 18:03
@andre, actually the result of the calculation is NOT a sequence it's a list, as both x and y are Lists (Hence the 1 in {x, _Real, 1} etc. The function works fine If I replace z with a number instead. So the problem is with Outer as there's no room for that third argument. – RunnyKine Feb 7 '13 at 18:17
must go outside. I See that in 2 hours. Sorry – andre Feb 7 '13 at 18:21
@RunnyKine: Could you give an example call to Outer with the minimumImagePD? – Joel Klein Feb 7 '13 at 18:24
@Joel Klein, that's exactly the reason I asked this question. There's no place for the third argument z to go. That's why I tried the 2nd variation of the function. minimumImagePD should accept List just like I showed with minimumImagePD2. – RunnyKine Feb 7 '13 at 18:28

The original definition of minimumImagePD can be used with Outer with this syntax:

Outer[minimumImagePD[##, zvalue] &, xlist, ylist, 1]


Outer provides two arguments to the pure function minimumImagePD[##, zvalue] &, and the pure function inserts those two arguments into the ## (SlotSequence), so that minimumImagePD is called with the expected three arguments in total.

-
This is exactly what I wanted. So elegant. Thanks – RunnyKine Feb 7 '13 at 21:24

I am not sure I understand 100% what you are looking for but does this help:

This inserts the value into the compiled function:

minimumImagePD2[zIn_Real] :=
With[{z = zIn},
Compile[{{x, _Real, 1}, {y, _Real, 1}},
Total[((x - y) - z*Round[(x - y)/z])^2]]]

f = minimumImagePD2[3.25026]


The use as before.

Outer[f, {{2.3, 4.3, 6.5}}, {{2.1, 4.8, 7.3}, {2.2, 1.1, 4.3}, {2.1, 3.3, 4.7}}, 1]
(* {{0.93, 1.11557, 3.14325}} *)


As suggested by OleksandrR, the With is not strictly necessary, so you can also go with

minimumImagePD2[z_Real] :=
Compile[{{x, _Real, 1}, {y, _Real, 1}},
Total[((x - y) - z*Round[(x - y)/z])^2]]]


I find the With solution clearer, but clearly that is a matter of taste.

-
this has been suggested above by Oleksandr R. It's just adding set-delayed to my minimumImagePD2 function. – RunnyKine Feb 7 '13 at 19:49
I am doing something somewhat different here; I generate the compiled function with the proper value inside and then use it as often as I want. Also this presetting allows for optimizations in compile – user21 Feb 7 '13 at 19:59
@OleksandrR., I'd leave it in; it's more general for other type of problems. – user21 Feb 7 '13 at 20:01

Here is a more elegant way, I think, to solve this problem. By including the third argument as one of the Lists in Outer.

minimumImagePD = Compile[{{x, _Real, 1}, {y, _Real, 1}, {z, _Real}},
Total[((x - y) - z * Round[ (x - y) / z] )^2]]


And using it like this:

Outer[minimumImagePD, {{2.3, 4.3, 6.5}}, {{2.1, 4.8, 7.3}, {2.3, 1.1,
4.3}, {2.1, 3.3, 4.7}}, {3.25026}, 1] // Flatten

-
This is also how I would do it. +1 – Mr.Wizard Mar 8 '13 at 2:40
@Mr.Wizard, thanks. – RunnyKine Mar 8 '13 at 3:33