使用 python class 的元胞自动机
Cellular Automata using python class
我制作了一个 class 来启动和更新 CA 数据,我制作了一个函数“Simulate
”,它根据火在树间蔓延的规则更新单元格,并且留下空白。根据给定的概率,用树木替换空白空间。
出现问题,我的函数似乎将规则应用于当前时间数据持有者,而不是先前的时间数据持有者。我已将 prevstate = self.state
设置为上一次迭代的临时数据保存器,但 运行 小测试我发现它给出的结果与我根本不包含此行时的结果相同。我做错了什么?
import numpy as np
import random
import matplotlib.pyplot as plt
from matplotlib.colors import LinearSegmentedColormap, colorConverter
from matplotlib.animation import FuncAnimation
#dimentions:
x = 10
y = 10
lighting = 0 #set to 0 for testing
grow = 0.3
#parameter values
empty = 0
tree = 1
fire = 2
random.seed(1)
#CA Rule definition
def update(mat, i, j, lighting, grow, prob):
if mat[i, j] == empty:
if prob < grow:
return tree
else:
return empty
elif mat[i, j] == tree:
if max(mat[i-1, j], mat[i+1, j], mat[i, j-1], mat[i, j+1]) == fire:
return fire
elif prob < lighting:
return fire
else:
return tree
else:
return empty
########## Data Holder
class Simulation:
def __init__(self):
self.frame = 0
#self.state = np.random.randint(2, size=(x, y)) commented out for testing
self.state = np.ones((x, y))
self.state[5, 5] = 2 #initial fire started at this location for testing
def updateS(self):
prevstate = self.state #line of code i think should be passing previous iteration through rule
for i in range(1, y-1):
for j in range(1, x-1):
prob = random.random()
self.state[i, j] = update(prevstate, i, j, lighting, grow, prob)
def step(self):
self.updateS()
self.frame += 1
simulation = Simulation()
figure = plt.figure()
ca_plot = plt.imshow(simulation.state, cmap='seismic', interpolation='bilinear', vmin=empty, vmax=fire)
plt.colorbar(ca_plot)
transparent = colorConverter.to_rgba('black', alpha=0)
#wall_colormap = LinearSegmentedColormap.from_list('my_colormap', [transparent, 'green'], 2)
def animation_func(i):
simulation.step()
ca_plot.set_data(simulation.state)
return ca_plot
animation = FuncAnimation(figure, animation_func, interval=1000)
mng = plt.get_current_fig_manager()
mng.window.showMaximized()
plt.show()
欢迎就实施 CA 的更好方法提出任何意见!
Python赋值是指针...所以当你更新self.state时,prevstate也会更新。
我希望你设置为:
prevstate = copy.copy(self.state)
这应该可以解决您的问题。
正如 jabberwocky 正确指出的那样,您的问题是行 prevstate = self.state
使 prevstate
成为对与 self.state
相同的 numpy 数组的新引用,因此修改其中一个的内容也会修改另一个。
然而,不是在每次迭代时都复制数组,而是一种稍微更有效的解决方案是预分配两个数组并交换它们,就像这样:
class Simulation:
def __init__(self):
self.frame = 0
self.state = np.ones((x, y))
self.state[5, 5] = 2
self.prevstate = np.ones((x, y)) # <-- add this line
def updateS(self):
self.state, self.prevstate = self.prevstate, self.state # <-- swap the buffers
for i in range(1, y-1):
for j in range(1, x-1):
prob = random.random()
self.state[i, j] = update(self.prevstate, i, j, lighting, grow, prob)
我说 "slightly" 因为您真正保存的只是一个 numpy 数组副本和垃圾收集器的一些工作。然而,如果你足够优化你的内部状态更新循环——也许例如使用 numba 实施 CA 规则——额外数组副本的相对成本将开始显着。无论如何,使用这种 "double buffering" 方法并没有真正的缺点,所以这是一个很好的习惯。
我制作了一个 class 来启动和更新 CA 数据,我制作了一个函数“Simulate
”,它根据火在树间蔓延的规则更新单元格,并且留下空白。根据给定的概率,用树木替换空白空间。
出现问题,我的函数似乎将规则应用于当前时间数据持有者,而不是先前的时间数据持有者。我已将 prevstate = self.state
设置为上一次迭代的临时数据保存器,但 运行 小测试我发现它给出的结果与我根本不包含此行时的结果相同。我做错了什么?
import numpy as np
import random
import matplotlib.pyplot as plt
from matplotlib.colors import LinearSegmentedColormap, colorConverter
from matplotlib.animation import FuncAnimation
#dimentions:
x = 10
y = 10
lighting = 0 #set to 0 for testing
grow = 0.3
#parameter values
empty = 0
tree = 1
fire = 2
random.seed(1)
#CA Rule definition
def update(mat, i, j, lighting, grow, prob):
if mat[i, j] == empty:
if prob < grow:
return tree
else:
return empty
elif mat[i, j] == tree:
if max(mat[i-1, j], mat[i+1, j], mat[i, j-1], mat[i, j+1]) == fire:
return fire
elif prob < lighting:
return fire
else:
return tree
else:
return empty
########## Data Holder
class Simulation:
def __init__(self):
self.frame = 0
#self.state = np.random.randint(2, size=(x, y)) commented out for testing
self.state = np.ones((x, y))
self.state[5, 5] = 2 #initial fire started at this location for testing
def updateS(self):
prevstate = self.state #line of code i think should be passing previous iteration through rule
for i in range(1, y-1):
for j in range(1, x-1):
prob = random.random()
self.state[i, j] = update(prevstate, i, j, lighting, grow, prob)
def step(self):
self.updateS()
self.frame += 1
simulation = Simulation()
figure = plt.figure()
ca_plot = plt.imshow(simulation.state, cmap='seismic', interpolation='bilinear', vmin=empty, vmax=fire)
plt.colorbar(ca_plot)
transparent = colorConverter.to_rgba('black', alpha=0)
#wall_colormap = LinearSegmentedColormap.from_list('my_colormap', [transparent, 'green'], 2)
def animation_func(i):
simulation.step()
ca_plot.set_data(simulation.state)
return ca_plot
animation = FuncAnimation(figure, animation_func, interval=1000)
mng = plt.get_current_fig_manager()
mng.window.showMaximized()
plt.show()
欢迎就实施 CA 的更好方法提出任何意见!
Python赋值是指针...所以当你更新self.state时,prevstate也会更新。
我希望你设置为:
prevstate = copy.copy(self.state)
这应该可以解决您的问题。
正如 jabberwocky 正确指出的那样,您的问题是行 prevstate = self.state
使 prevstate
成为对与 self.state
相同的 numpy 数组的新引用,因此修改其中一个的内容也会修改另一个。
然而,不是在每次迭代时都复制数组,而是一种稍微更有效的解决方案是预分配两个数组并交换它们,就像这样:
class Simulation:
def __init__(self):
self.frame = 0
self.state = np.ones((x, y))
self.state[5, 5] = 2
self.prevstate = np.ones((x, y)) # <-- add this line
def updateS(self):
self.state, self.prevstate = self.prevstate, self.state # <-- swap the buffers
for i in range(1, y-1):
for j in range(1, x-1):
prob = random.random()
self.state[i, j] = update(self.prevstate, i, j, lighting, grow, prob)
我说 "slightly" 因为您真正保存的只是一个 numpy 数组副本和垃圾收集器的一些工作。然而,如果你足够优化你的内部状态更新循环——也许例如使用 numba 实施 CA 规则——额外数组副本的相对成本将开始显着。无论如何,使用这种 "double buffering" 方法并没有真正的缺点,所以这是一个很好的习惯。