Fill a multidimensional array efficiently that have many if else statements
<p>I want to fill an 4dim numpy array in a specific and efficient way. Because I don't know better I startet to write the code with if else statements, but that doesn't look nice, is probably slow and I also can not be really sure if I thought about every combination. Here is the code which I stopped writing down:</p>

<pre><code>sercnew2 = numpy.zeros((gn, gn, gn, gn))
for x1 in range(gn):
    for x2 in range(gn):
        for x3 in range(gn):
            for x4 in range(gn):
                if x1 == x2 == x3 == x4: 
                    sercnew2[x1, x2, x3, x4] = ewp[x1]
                elif x1 == x2 == x3 != x4:
                    sercnew2[x1, x2, x3, x4] = ewp[x1] * ewp[x4]
                elif x1 == x2 == x4 != x3:
                    sercnew2[x1, x2, x3, x4] = ewp[x1] * ewp[x3]
                elif x1 == x3 == x4 != x2:
                    sercnew2[x1, x2, x3, x4] = ewp[x1] * ewp[x2]
                elif x2 == x3 == x4 != x1:
                    sercnew2[x1, x2, x3, x4] = ewp[x2] * ewp[x1]
                elif x1 == x2 != x3 == x4:
                    sercnew2[x1, x2, x3, x4] = ewp[x1] * ewp[x3]
                elif ... many more combinations which have to be considered
</code></pre>

<p>So basically what should happen is, that if all variables (x1, x2, x3, x4) are different from each other, the entry would be: </p>

<pre><code>sercnew2[x1, x2, x3, x4] = ewp[x1]* ewp[x2] * ewp[x3] * ewp[x4]
</code></pre>

<p>Now if lets say the variable x2 and x4 is the same then:</p>

<pre><code>sercnew2[x1, x2, x3, x4] = ewp[x1]* ewp[x2] * ewp[x3]
</code></pre>

<p>Others examples can be seen in the code above. Basically if two or more variables are the same, then I only consider on of them. I hope the pattern is clear. Otherwise please let me note and I will try to express my problem better. I am pretty sure, that there is a much more intelligent way to do it. Hope you know better and thanks in advance :)   </p>

<p>You can do it in a single for loop too. Building on Divakar's Trick for a list of indexes, the first thing we need to do is figure out how to extract only the unique indices of a given element in the 4d array <code>sercnew2</code>.</p>

<p>One of the fastest ways to do this (Refer: <a href="https://www.peterbe.com/plog/uniqifiers-benchmark" rel="nofollow">https://www.peterbe.com/plog/uniqifiers-benchmark</a>) is using sets. Then, we simply have to initialize <code>sercnew2</code> as an array of ones, rather than zeros.</p>

<pre><code>from itertools import product
import numpy as np

sercnew2 = np.ones((gn, gn, gn, gn))
n_dims=4
idx = list(product(np.arange(gn), repeat=n_dims))

for i,j,k,l in idx:
    unique_items = set((i,j,k,l))
    for ele in unique_items:
        sercnew2[i,j,k,l] *= ewp[ele]
</code></pre>

<p>EDIT: As @unutbu suggested, we could also use the cartesian_product function from <a href="http://stackoverflow.com/a/11146645/5714445">http://stackoverflow.com/a/11146645/5714445</a> to speed up the initialization of <code>idx</code></p>

<p>Edit2: In case you are having difficulty understanding what <code>product</code> from <code>itertools</code> does, it provides all permutations. For instance, suppose <code>gn=2</code>, with repeat dimension set to 4, you get</p>

<pre><code>[0, 0, 0, 0]
[0, 0, 0, 1]
[0, 0, 1, 0]
[0, 0, 1, 1]
[0, 1, 0, 0]
[0, 1, 0, 1]
[0, 1, 1, 0]
[0, 1, 1, 1]
[1, 0, 0, 0]
[1, 0, 0, 1]
[1, 0, 1, 0]
[1, 0, 1, 1]
[1, 1, 0, 0]
[1, 1, 0, 1]
[1, 1, 1, 0]
[1, 1, 1, 1]
</code></pre>

python arrays numpy