I have made a class which initiates and updates the CA data, and I have made a function ' Simulate
' which updates the cells based on the rule that fire spreads across trees, and leaves empty spaces. Empty spaces are replaced with trees based on a given probability.
There is a problem where it appears my function is applying the rule to the current time data holder, rather than the previous time data holder. I have set prevstate = self.state
to act as a temporary data holder for the previous iteration, but running small tests I find that it gives the same results as if I didn't include this line at all. What am I doing wrong?
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()
Any comments on better ways to implement a CA are most welcome!
Python assignments are pointers... So when you update self.state, then prevstate is also updated.
I expect if you set to:
prevstate = copy.copy(self.state)
That should fix your problem.
As jabberwocky correctly notes, your problem is that the line prevstate = self.state
makes prevstate
a new reference to the same numpy array as self.state
, so that modifying the contents of one also modifies the other.
Instead of copying the array on every iteration, however, a slightly more efficient solution would be to preallocate two arrays and swap them, something like this:
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)
I say "slightly" because all you're really saving is a numpy array copy and some work for the garbage collector. However, if you optimize your inner state update loop enough — maybe eg implementing the CA rule using numba — the relative cost of an extra array copy will start to be significant. In any case, there are no real down sides to using this "double buffering" method, so it's a good habit to pick up.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.