# Efficiently Evaluate a Multivariable Function on a List of Tuples

I have a function G[x,y,z,r,s,t] in the code below. Quite simply, I want to generate a list of all possible tuples $(x,y,z,r,s,t)$ such that all six entries are taken from the set $\{0,1,2,...,N-1\}$ where $N$ will be an integer parameter of the problem. I then want to evaluate $G$ on each of these tuples and as efficiently as I can, count the number of the tuples which evaluate to 0 modulo $N$. I at least have the following:

G[x_, y_, z_, r_, s_, t_] := x*y*z*(r^3 + s^3 + t^3) - r*s*t*(x^3 + y^3 + z^3); F[N_] := Range[0, N - 1]; Tup[N_] := Tuples[F[N], 6];

So Tup[N] is a list of all my tuples of interest. I was about to do a "for loop" ranging over the number of tuples and evaluate something like

G[Tup[2][[4]][[1]], Tup[2][[4]][[2]], Tup[2][[4]][[3]], Tup[2][[4]][[4]], Tup[2][[4]][[5]], Tup[2][[4]][[6]]]

for example, but this seems to be extremely inefficient. I'm sure there must be a smarter way! So given my G[x,y,z,r,s,t] as well as Tup[N] how can I construct a function P[N] which will output the number of tuples which evaluate to 0 modulo $N$?

• Try G @@@ Tup[10]; or G @@ Transpose[Tup[10]];. The latter should be faster but may not always work. – Henrik Schumacher Sep 16 '18 at 21:11

Here are several ways to perform the computations along with timings:

G2[X_] := X[[1]] X[[2]] X[[3]] (X[[4]]^3 + X[[5]]^3 + X[[6]]^3) -
X[[4]] X[[5]] X[[6]] (X[[1]]^3 + X[[2]]^3 + X[[3]]^3);
cG2 = With[{code = G2[Array[CompileGetElement[X, #] &, {6}]]},
Compile[{{X, _Integer, 1}},
code,
CompilationTarget -> "C",
RuntimeAttributes -> {Listable},
Parallelization -> True,
RuntimeOptions -> "Speed"
]
];

data = Tup[10];
a = G @@@ data; // RepeatedTiming // First
b = G @@ Transpose[data]; // RepeatedTiming // First
c = G2 /@ data; // RepeatedTiming // First
d = cG2[data]; // RepeatedTiming // First
e = Flatten[Outer[G, ## & @@ ConstantArray[F[10], 6]]]; // RepeatedTiming // First
a == b == c == d == e


4.012

0.068

7.30

0.0312

3.80

True

• wow, the compilation really does rule! any insight on why a and b have such differing runtimes? – Ben Kalziqi Sep 16 '18 at 21:37
• Well, b heavily uses vectorization and d is compiled. So, a and c have to struggle with the fact that Mathematica is an interpreted language, not a compiled one. They are also not parallelized. I am a bit surprised that c is so much slower than a – Henrik Schumacher Sep 16 '18 at 21:44
• Beautiful! Thanks a lot. I appreciate the multiple solutions. – Benighted Sep 16 '18 at 23:11
• You're welcome. – Henrik Schumacher Sep 17 '18 at 4:06
tup1[N_] := Tuples[G @@ F[N], 6]


or

tup2[N_] := Flatten[Outer[G, ## & @@ ConstantArray[F[N], 6]]]


They both give the same result as G @@@ Tup[N] suggested by Henrik:

tup1[7] == tup2[7] == G @@@ Tup[7]
`

True