1

I have a matrix with the shape (3*k, 3*l) (e.g.: k=2, l=1):

A = np.arange(18).reshape(6, 3)
array([[ 0,  1,  2],
       [ 3,  4,  5],
       [ 6,  7,  8],
       [ 9, 10, 11],
       [12, 13, 14],
       [15, 16, 17]])

Now I want to expand this to a matrix 3*k, 3*l*3 (e.g.: 6x9) while flipping the elements for every 3-sized blocks:

B = np.asarray([[ 0.,  3.,  6.,  1.,  4.,  7.,  2.,  5.,  8.],
                [ 6.,  0.,  3.,  7.,  1.,  4.,  8.,  2.,  5.],
                [ 3.,  6.,  0.,  4.,  7.,  1.,  5.,  8.,  2.],
                [ 9., 12., 15., 10., 13., 16., 11., 14., 17.],
                [15.,  9., 12., 16., 10., 13., 17., 11., 14.],
                [12., 15.,  9., 13., 16., 10., 14., 17., 11.]])

To expand the matrix, I used the Kronecker product (of numpy):

K = np.kron(A, np.ones(3))
array([[ 0.,  0.,  0.,  1.,  1.,  1.,  2.,  2.,  2.],
       [ 3.,  3.,  3.,  4.,  4.,  4.,  5.,  5.,  5.],
       [ 6.,  6.,  6.,  7.,  7.,  7.,  8.,  8.,  8.],
       [ 9.,  9.,  9., 10., 10., 10., 11., 11., 11.],
       [12., 12., 12., 13., 13., 13., 14., 14., 14.],
       [15., 15., 15., 16., 16., 16., 17., 17., 17.]])

On this matrix I want to flip the rows tripple-wise for every column, where the column index modulo 3 is not 0. I thought of multiplying with some kind of rotation matrices would help such as:

r0 = np.asarray([[1, 0, 0], 
                 [0, 1, 0], 
                 [0, 0, 1]])
r1 = np.asarray([[0, 1, 0], 
                 [0, 0, 1], 
                 [1, 0, 0]])
r2 = np.asarray([[0, 0, 1], 
                 [1, 0, 0], 
                 [0, 1, 0]])

R = np.asarray([r0, r1, r2])

But I didn't get the expected result. So my question is:

How to calculate flipping of tripple-wise rows?

or is there a better/faster way, to expand and tripple-wise flip the rows?

  • 1
    think in terms of making a new array, and assigning different blocks. Reshaping the original to (2,3,3) might make this easier, since you are 'rotating' blocks of size (3,3). At least for a start try to get small units right. A 'fast' global solution may impossible or at least hard to visualize. – hpaulj Feb 25 '23 at 02:09
  • Thanks, that helped! After reshaping like you said `B = A.reshape((-1, 3, A.shape[1]))`. I got the Idea of using tensor dot product `D = np.tensordot(B, R, [1,0])` to get the correct values and than reshaped to the correct matrix: `D.swapaxes(1, 2).reshape((-1, 3*A.shape[1]))`. – Olivér Palotás Feb 25 '23 at 04:14

1 Answers1

1

You could do this:

def triple_flip(A):
    orig_shape = A.shape
    outer_rows = A.shape[0] // 3
    inner_rows = 3
    n_cols = A.shape[1]
    # Check that we're not dropping rows
    assert A.shape[0] == outer_rows * inner_rows
    A = A.reshape(outer_rows, inner_rows, n_cols)
    # For each 3x3 group,
    #  roll first col 0 rows down
    #  roll second col 2 rows down
    #  roll third col 1 row down
    shift_constant = np.array([0, 2, 1])
    # Tile shift constant to have as many columns as are in A
    r = np.tile(shift_constant, n_cols // 3)
    rows, columns = np.ogrid[:inner_rows, :n_cols]
    # Shift rows by r, broadcasting
    rows = rows - r[np.newaxis, :]
    A = A[:, rows, columns]
    A = A.reshape(outer_rows * inner_rows, n_cols)
    assert A.shape == orig_shape
    return A

Explanation: this is essentially the same as this question, with two complications:

  1. You want to roll columns, not rows.
  2. You want to roll each 3x3 group of numbers independently.

You can solve #1 by adjusting the axis and #2 by reshaping prior to doing the roll.

Nick ODell
  • 15,465
  • 3
  • 32
  • 66