如何计算3d数组的所有24个旋转?

Col*_*nic 19 python numpy

我有一个3d numpy数组描述一个多维数据集(想象一个3d俄罗斯方块).如何计算所有24个旋转?

Numpy的数组操作例程包含一个rot90方法,该方法给出了24个中的4个,但是我对如何计算其余部分毫无头绪.我唯一的想法是将3d数组转换为坐标的2d矩阵,乘以旋转矩阵,然后转换回来.但我宁愿直接使用3d数组.

示例2x2x2数组:

>>> from numpy import array
>>> polycube
array([[[1, 0],
        [1, 0]],

       [[1, 1],
        [0, 0]]])
Run Code Online (Sandbox Code Playgroud)

示例3x3x3数组:

array([[[1, 1, 0],
        [1, 1, 0],
        [0, 0, 0]],

       [[0, 0, 0],
        [1, 0, 0],
        [1, 0, 0]],

       [[0, 0, 0],
        [0, 0, 0],
        [0, 0, 0]]])
Run Code Online (Sandbox Code Playgroud)

编辑:我只想要24个方向保持等距,而不是所有48个旋转和反射(虽然知道如何制作它们也会很有趣).如果它有助于测试,我相信3x3x3示例没有旋转对称性并且是手性的(因此48是不同的).

动机:我正在为Soma立方体式拼图编写解算器.

Dan*_*l F 8

编辑:由于我的解决方案基本上归结为轴的奇偶校验乘以轴的排列奇偶校验的乘积,生成 n 维数组的所有常规旋转的最简单方法是这样的(刷一些代码形成@Divakar的答案):

import itertools as it

def p_parity(a):
    a = np.asarray(a)
    l = a.size
    i, j = np.tril_indices(l, -1)
    return np.product(np.sign(a[i] - a[j]))

def rotations_gen(m):
    n = m.ndim
    for i in it.product([-1, 1], repeat = n):
        for p in it.permutations(np.arange(n)):
            if np.product(i) * p_parity(p) == 1:
                s = [slice(None, None, j) for j in i]
                yield np.transpose(m[s], p)    
Run Code Online (Sandbox Code Playgroud)

这适用于任意维度的任何(甚至非方形)张量,并且直接基于下面张量代数下规则旋转的定义。

背景

解释这一点的最简单方法是用张量术语,因此让我们将所有这些旋转转换为旋转张量。旋转张量是n x n旋转 n 维空间的矩阵。因此,它们有一些属性:

np.linalg.det(R) == 1                    # determinant = 1
np.inner(R, R.T) == np.eye(R.shape[0])   # Transpose is inverse
Run Code Online (Sandbox Code Playgroud)

此外,对于 90 度旋转,所有项必须为 0、1 或 -1。

在三维空间中,存在三个基本族,它们组合在一起形成 24 圈旋转。

第一个是简单的排列:

A = 
[[[1, 0, 0],
  [0, 1, 0],
  [0, 0, 1]],

 [[0, 1, 0],
  [0, 0, 1],
  [1, 0, 0]],

 [[0, 0, 1],
  [1, 0, 0],
  [0, 1, 0]]]
Run Code Online (Sandbox Code Playgroud)

第二个涉及对一些项取负,以便对角线的乘积始终为 1:

B = 
[[[ 1, 0, 0],
  [ 0, 1, 0],
  [ 0, 0, 1]],

 [[-1, 0, 0],
  [ 0,-1, 0],
  [ 0, 0, 1]],

 [[-1, 0, 0],
  [ 0, 1, 0],
  [ 0, 0,-1]],

 [[ 1, 0, 0],
  [ 0,-1, 0],
  [ 0, 0,-1]]]
Run Code Online (Sandbox Code Playgroud)

第三个确定排列是正数还是负数,如果是负数则否定这些项

C = 
[[[ 1, 0, 0],
  [ 0, 1, 0],
  [ 0, 0, 1]],

 [[ 0, 0,-1],
  [ 0,-1, 0],
  [-1, 0, 0]],
Run Code Online (Sandbox Code Playgroud)

这些系列的重要之处在于,在每个系列中,两个矩阵的任何乘积、幂或转置都会产生该系列中的另一个矩阵。由于我们有三个族,它们彼此的乘积形成了所有可能的旋转,在本例中为 3*4*2 = 24

注意:其他 24 个“不规则”旋转是相同的矩阵相乘,-np.eye(3)得到行列式 = -1 的相似矩阵

应用

这一切都很好,但这与数组操作有什么关系呢?我们不想通过矩阵乘法进行旋转,因为这会导致内存和处理方面的过度开销。幸运的是,每个系列都很容易与生成视图的数组操作相关。

def A_(m, i):  # i in (0, 1, 2)
    idx = np.array([[0, 1, 2], [1, 2, 0], [2, 0, 1]])
    return np.transpose(m, idx[i])

def B_(m, j):  # j in (0, 1, 2, 3)
    idx = np.array([[ 1, 1, 1],
                    [ 1,-1,-1],
                    [-1, 1,-1],
                    [-1,-1, 1]])
    return m[::idx[j, 0], ::idx[j, 1], ::idx[j, 2]]

def C_(m, k):  # k in (1, -1)
    return np.transpose(m, np.arange(3)[::k])[::k, ::k, ::k]
Run Code Online (Sandbox Code Playgroud)

所有这些都会生成 的视图m,您可以创建一个生成器,通过以下方式生成与所有旋转相关的视图:

def cube_rot_gen(m):
    for i in [0, 1, 2]:
        for j in [0, 1, 2, 3]:
            for k in [1, -1]:
                yield C_(B_(A_(m, i), j), k)
Run Code Online (Sandbox Code Playgroud)


hpa*_*ulj 7

看看代码rot90.我看到3点的变化flipswapaxes根据k该轴参数.

fliplr(m).swapaxes(0, 1)
fliplr(flipud(m))
fliplr(m.swapaxes(0, 1))
Run Code Online (Sandbox Code Playgroud)

fliplr(m)只是m[:, ::-1],而不是令人惊讶的,flipudm[::-1, ...].

你可以用m[:,:,::-1],或者翻转第3轴m[...,::-1].

np.transpose是另一种用于置换轴的工具,可能会或可能不会比使用更容易swapaxes.

如果rot90给你4个旋转,你应该能够应用相同的例程来产生其他的旋转.你只需要了解潜在的逻辑rot90.

例如

def flipbf(m):
    return m[:,:,::-1]

flipbf(m).swapaxes(0, 2)
flipbf(m).swapaxes(1, 2)
etc
Run Code Online (Sandbox Code Playgroud)


Col*_*nic 6

到目前为止,我有12个,组成numpy.transpose以置换轴(xyz,yzx,zxy-所有相同的手性)和rot90.

def rotations12(polycube):
    for i in range(3):
        polycube = numpy.transpose(polycube, (1, 2, 0))
        for angle in range(4):
            polycube = numpy.rot90(polycube)
            yield polycube
Run Code Online (Sandbox Code Playgroud)

快速测试12是截然不同的: len(set(str(x) for x in rotations(polycube)))


更新:这是我如何制作所有24.

def rotations24(polycube):
    # imagine shape is pointing in axis 0 (up)

    # 4 rotations about axis 0
    yield from rotations4(polycube, 0)

    # rotate 180 about axis 1, now shape is pointing down in axis 0
    # 4 rotations about axis 0
    yield from rotations4(rot90(polycube, 2, axis=1), 0)

    # rotate 90 or 270 about axis 1, now shape is pointing in axis 2
    # 8 rotations about axis 2
    yield from rotations4(rot90(polycube, axis=1), 2)
    yield from rotations4(rot90(polycube, -1, axis=1), 2)

    # rotate about axis 2, now shape is pointing in axis 1
    # 8 rotations about axis 1
    yield from rotations4(rot90(polycube, axis=2), 1)
    yield from rotations4(rot90(polycube, -1, axis=2), 1)

def rotations4(polycube, axis):
    """List the four rotations of the given cube about the given axis."""
    for i in range(4):
        yield rot90(polycube, i, axis)
Run Code Online (Sandbox Code Playgroud)

使用这个辅助函数推广rot90绕任何轴旋转:

def rot90(m, k=1, axis=2):
    """Rotate an array k*90 degrees in the counter-clockwise direction around the given axis"""
    m = numpy.swapaxes(m, 2, axis)
    m = numpy.rot90(m, k)
    m = numpy.swapaxes(m, 2, axis)
    return m
Run Code Online (Sandbox Code Playgroud)

我不相信这个助手是完美的,但似乎有效.