Efficiently Evaluate a Multivariable Function on a List of Tuples
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]
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];
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
Tup[N]
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]]]
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$?
G[x,y,z,r,s,t]
Tup[N]
P[N]
G @@@ Tup[10];
G @@ Transpose[Tup[10]];
2 Answers
2
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[Compile`GetElement[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
$begingroup$
wow, the compilation really does rule! any insight on why
a
and b
have such differing runtimes?$endgroup$
– Ben Kalziqi
Sep 16 '18 at 21:37
a
b
$begingroup$
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
$endgroup$
– Henrik Schumacher
Sep 16 '18 at 21:44
b
d
a
c
c
a
$begingroup$
Beautiful! Thanks a lot. I appreciate the multiple solutions.
$endgroup$
– Benighted
Sep 16 '18 at 23:11
$begingroup$
You're welcome.
$endgroup$
– 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:
G @@@ Tup[N]
tup1[7] == tup2[7] == G @@@ Tup[7]
True
Thanks for contributing an answer to Mathematica Stack Exchange!
But avoid …
Use MathJax to format equations. MathJax reference.
To learn more, see our tips on writing great answers.
Required, but never shown
Required, but never shown
By clicking "Post Your Answer", you agree to our terms of service, privacy policy and cookie policy
$begingroup$
Try
G @@@ Tup[10];
orG @@ Transpose[Tup[10]];
. The latter should be faster but may not always work.$endgroup$
– Henrik Schumacher
Sep 16 '18 at 21:11