pythonnumpyvectorization

Summarize higher dimensions in numpy


I have a numpy array that holds board game states for all possible moves, and I want to summarize some of those moves. I'm struggling to vectorize that code and avoid a for loop when I choose which moves I want to summarize.

Here's a simplified example of what I'm trying to do. I create a 3x3x3x3 array that represents the spaces that could be attacked by a queen at each board square of a 3x3 chess board. In other words, the first two dimensions are the coordinates of a queen on the board, and the last two dimensions are boolean flags of whether the queen could attack that square.

Then I select some squares, and count up how many of those squares could attack each square of the board. That counting step is what I'm trying to do without a Python for loop.

Here's the example code:

import numpy as np

size = 3
patterns = np.zeros((size, size, size, size), dtype=bool)
for i in range(size):
    for j in range(size):
        patterns[i, j, i, :] = True
        patterns[i, j, :, j] = True
        for i2 in range(size):
            shift = i2 - i
            j2 = j + shift
            if 0 <= j2 < size:
                patterns[i, j, i2, j2] = True
            j3 = j - shift
            if 0 <= j3 < size:
                patterns[i, j, i2, j3] = True

active_positions = np.array([[0, 1, 0],
                             [1, 0, 0],
                             [0, 0, 0]], dtype=bool)

# This is the part I want to vectorize:
counts = np.zeros((size, size))
for i in range(size):
    for j in range(size):
        if active_positions[i, j]:
            counts += patterns[i, j]

print(counts)

Is there a way to do that counting without using a for loop?


Solution

  • Use active_positions to mask patterns and sum along the zeroth (first) axis (that will be an element-wise sum of each field, which in this case is two 3x3 arrays).

    counts = patterns[active_positions].sum(0)
    

    Consider the intermediate array patterns[active_positions]; this has shape (2,3,3). Summing along the zeroth axis adds the two 3x3 arrays in an element-wise manner to produce the final (3,3) array that you're looking for.