# Loop over multiple variables with readable code

I've seen similar posts to this, but not with as many variables. I need to loop over 8 variables, with only two values each(1 and 2), so I can loop over the 16 possible input and output states, and write the symbolic results in a 16x16 matrix. Instead of just nesting 8 for loops, is there a more readable way?

Edit: I'm sorry I didn't include the code needed to actually help me, this is my first time posting here. The goal is to evaluate the function

takeOpVal[O1_, O2_, O_ 3, O_ 4, O_ 5, O_ 6, O_ 7, O_ 8, U_] := Tr[KroneckerProduct[O1, O2, O3, O4] . U . KroneckerProduct[O5, O6, O7, O8] . ConjugateTranspose[U]];


for each combination of the 2x2 Identity matrix and Pauli Z matrix. I figured it would be easiest to create a "translator":

Translator = {Id, Sz}


and then simply call the function takeOpVal with Translator{i},Translator{j} etc and iterate over the variables.

• Welcome to the Mathematica Stack Exchange. Could you please reduce your problem to two or three variables with two values each and show what the desired output matrix would look like? This will make the question more concrete and extensible to the eight variable case. Thanks.
– Syed
Jun 9, 2022 at 7:46
• Welcome! In general, you shouldn't use For loops in mathematica, especially when building tables! See Avoiding For Loops. (Its parent post is always useful too!) It's often better to use Table or Array. Table can use multiple loops, e.g. t = Table[f[i,j,k,l,m,n,o,p], {i,2}, {j,2}, {k,2}, {l,2}, {m,2}, {n,2}, {o,2}, {p,2}]. Then to get a 16x16 array, you can use ArrayReshape[t, {16, 16}], or Flatten if you need more control over which layer winds up where. Jun 9, 2022 at 8:00
• However, a more concise way is to use Tuples, then reshape: Tuples[{1, 2}, 8] will produce a list of all possible combinations, then (if your function takes in 8 arguments) you can apply a function to each of these using f @@@ Tuples[{1, 2}, 8] (see the docs for Apply). If your function takes in a list of 8 values, use Map instead, e.g. f /@ Tuples[{1,2}, 8]. Then use ArrayReshape as before on the result. Jun 9, 2022 at 8:03
• Also, it's good practice on this forum to provide the code you're working with so that people can copy-paste to see what works! :) Jun 9, 2022 at 8:06
• @thorimur sorry for missing too much of the information, I have edited the post and include it now, thanks! Jun 9, 2022 at 9:30

(I'm repeating some things I said in a comment just for the sake of completeness :) )

Welcome! In general, you shouldn't use For loops in mathematica, especially when building tables! See Avoiding For Loops. (Its parent post is always useful too!)

It's often better to use Table or Array to generate arrays. Table can use multiple loops, e.g.

t = Table[f[i,j,k,l,m,n,o,p], {i,2}, {j,2}, {k,2}, {l,2}, {m,2}, {n,2}, {o,2}, {p,2}]


Then to get a 16x16 array, you can use ArrayReshape[t, {16, 16}], or Flatten if you need more control over which layer winds up where. (If you're new to Mathematica, note that Mathematica has generally great documentation, which can be accessed by clicking Help at the top-right of the notebook window; lots of further info on e.g. how to use Flatten in different ways can be found there.)

However, a more concise way is to use Tuples, then reshape: Tuples[{1, 2}, 8] will produce a list of all possible combinations, then (if your function takes in 8 arguments) you can apply a function to each of these using f @@@ Tuples[{1, 2}, 8] (see the docs for Apply). f @@@ {{a,b,c}, {d,e,g}} replaces the inner List with your function, and will evaluate to {f[a,b,c], f[d,e,g],}. (Contrast with Map (/@) which prefixes f to the list itself: f /@ {{a,b,c}, {d,e,g}} evaluates to {f[{a,b,c}], f[{d,e,g}]}.)

The definition you included was for takeOpVal[O1_, O2_, O3_, O4_, O5_, O6_, O7_, O8_, U_]. (Note: the code you included had errors, e.g. O_ 3 instead of O3_. Make sure these errors aren't present in your actual code!) Seeing as you want to sort of "parametrize" this by U, I recommed using the following pattern:

takeOpVal[U_][O1_, O2_, O3_, O4_, O5_, O6_, O7_, O8_] := (* ... *)


This behaves the same way in the end, but is more convenient: we can construct takeOpVal[U] which then behaves like a function that we can apply to sequences of the 8 O arguments. (If you're familiar, this is Mathematica's built-in version of currying.)

Note that Tuples works with any list! Tuples[{Bigthing1, Bigthing2, ...}, 8] will generate all 8-tuples with elements drawn from the list given as a first argument! So we can just use your Translator list.

(Note: if you're coming from another language, you may be used to using list{i} for part access as I noticed you did in the post! Remember that here, you use double square brackets: list[[i]]. See the docs for Part for more.)

So, to put it all together:

takeOpVal[U_][O1_, O2_, O3_, O4_, O5_, O6_, O7_, O8_] := Tr[KroneckerProduct[O1, O2, O3, O4] . U . KroneckerProduct[O5, O6, O7, O8] . ConjugateTranspose[U]]

Id = IdentityMatrix[2];
Sz = PauliMatrix[3];
Translator = {Id, Sz};
(* U = YourMatrixHere *)

vals = takeOpVal[U] @@@ Tuples[Translator, 8];

ArrayReshape[vals, {16,16}]


There may be technicalities for making this line up with KroneckerProduct, though. I'm not sure exactly how you want it reshaped. If you'd like to provide more detail, I'd be happy to help further. :)

• Thank you so much for the help, with your reply I got it to work! As you said, I'm coming from a different language and started learning Mathematica only a couple days ago, so things like the currying will take some time to understaand, but Ill get there. Thanks again for all the time you took to reply in such detail! Jun 12, 2022 at 7:41