numpy矢量化并行更新

时间:2014-12-01 23:17:45

标签: python numpy

我想用一个简单的规则来实现一维元胞自动机:

  1. 如果单元格 1 且相邻单元格 0 ,则移动(向右)
  2. 如果单元格 1 且相邻单元格 1 ,则不要移动
  3. 所有单元格根据其状态同时更新。
  4. 我们有封闭边界条件。这意味着最后一个单元的邻居是第一个单元。
  5. 例如:

    0 1 1 0 1

    更新后:

    1 1 0 1 0

    我的解决方案是

    def update(cells):
        neighbors = np.roll(cells,-1)
        dim = len(cells)
        tmp_cells = np.zeros(dim)
        for i,j in  enumerate(cells):
            if j and not neighbors[i]:
                tmp_cells[i], tmp_cells[(i+1)%dim] = 0, 1
            elif j:
                tmp_cells[i] = 1
        return tmp_cells
    

    这很好,但解决方案没有利用np.arrays的所有可能性,而是简化为list - 算法。

    我以为我可以在cellsneighbors之间找到一个简洁的逻辑,但显然我现在必须睡觉了。

    一些想法?

3 个答案:

答案 0 :(得分:5)

要获取没有循环的单元格的值,您需要知道两个侧的邻居。您需要左侧,因为如果您是0,则新值取决于您的左邻居,而如果您是1,则新值取决于您的右邻居。

你可以详尽地写出所有3个细胞的组合,对吧?换句话说:

000 -> 0
001 -> 0
010 -> 0 # move to the right
011 -> 1 # stay put
100 -> 1 # left neighbor has moved
101 -> 1 # left neighbor has moved
110 -> 0 # move to the right
111 -> 1 # stay put

您可以非常轻松地将该表转换为布尔函数。我们可以简化它,但让我们开始时只是愚蠢:-x & y & z | x & -y & -z | x & -y & z | x & y & z

就是这样:

left = np.roll(cells, -1)
right = np.roll(cells, 1)
return (np.logical_not(left) & cells & right | # ...)

当然,你现在想要简化布尔方程,*但这应该让你开始。

*或者可以退一步重新考虑规则。如果您是0,则始终会从您的左邻居复制新值;如果你是1,它总是从你的右邻居复制。您可以使用布尔运算符的组合来编写它,但使用屏蔽赋值可能更简单:result[cells] = left[cells]; result[notcells] = right[notcells]

答案 1 :(得分:1)

按照abarnert的建议滚动会起作用,但会创建两个副本,从性能角度看这不是理想的。

为了获得最佳性能,最好使用视图。像这样:

import numpy as np

cells = [0, 1, 1, 0, 1]
cells = np.concatenate([[0],cells, [0]])    #add padding cells
def boundary(cells):
    """enforce boundary conditions"""
    cells[0] = cells[-2]
    cells[-1] = cells[1]
    return cells
cells = boundary(cells)

centre = cells[1:-1]
left   = cells[0:-2]
right  = cells[2:]
#add your logic here...
print (np.logical_not(left) & centre & right)

答案 2 :(得分:0)

基于 Eelco Hoogendoorn 关于观看效果的评论以及 abarnet 的重要逻辑提示,这里的解决方案对我有用:

def update_parallel(cells):
    cells = boundary(cells)
    center = cells[1:-1]
    left = cells[0:-2]
    right = cells[2:]
    ones = (center == 1)
    zeros = (center == 0)
    result = np.copy(center)
    result[zeros] = left[zeros]
    result[ones] = right[ones]
    num_moves = sum(np.logical_xor(center, result)) / 2   # number of moves per step
    return result, num_moves