Why does my Game of Life simulation slow down to a crawl within seconds? Matplotlib to blame?

后端 未结 2 399
南方客
南方客 2021-01-25 07:57

I\'m learning OOP in python and so for a bit of fun I bashed out a GameOfLife simulator this morning. When it starts up it runs at about 20 cycles per second (due to the p

相关标签:
2条回答
  • 2021-01-25 08:18

    You will see that the images of previous calls are still present by printing the number of images inside the plot function,

    print ( len(plt.gca().images) )
    

    In your case this number will increase steadily, even though you delete the image, because it is still part of the axes and hence get redrawn every iteration.

    It would be much better to draw the image once, and then only update its data.

    class GoL():
        # ...
    
        def __init__(self, width, height, p=0.3):
            self.width = width
            self.height = height
            self.matrix = np.random.choice(a=[1, 0], size=(width, height), p=[p, 1 - p])
            self.im = plt.imshow(self.matrix)
    
        #  ....
    
        def plot(self):
            self.im.set_data(self.matrix)
            plt.pause(0.05)
            print len(plt.gca().images)
    

    This will result in an animation at constant speed.


    Concerning the question on where to improve the code, there are two things:

    1. use matplotlib.animation.FuncAnimation, as this is much more stable and lets you savely terminate the animation.
    2. use conditions on numpy arrays instead of looping over all pixels of the matrix.

    Full code:

    import matplotlib.pyplot as plt
    import numpy as np
    from scipy.ndimage import convolve
    import matplotlib.animation as animation
    
    class GoL():
    
        KERNEL = np.array([[1, 1, 1],
                           [1, 0, 1],
                           [1, 1, 1]])
    
        def __init__(self, width, height, p=0.3):
            self.width = width
            self.height = height
            self.matrix = np.random.choice(a=[1, 0], size=(width, height), p=[p, 1 - p])
            self.im = plt.imshow(self.matrix)
    
        def play(self):
            self.plot()
            self.ani= animation.FuncAnimation(plt.gcf(), self.cycle, repeat=True, interval=50 )
            plt.show()
    
        def cycle(self, n):
            c = self.eval()
            new = np.zeros_like(self.matrix)
            new[c == 3] = 1
            new[c < 2] = 0
            new[c > 3] = 0
            new[(self.matrix == 1) & (c == 2)] = 1
            self.matrix = new
            self.plot()
    
        def eval(self):
            return convolve(self.matrix, self.KERNEL, mode='constant')
    
        def plot(self):
            self.im.set_data(self.matrix)
    
    gol = GoL(width=100,height=100)
    gol.play()
    
    0 讨论(0)
  • 2021-01-25 08:25

    Before reusing the canvas, you should clear the old figure. You can use matplotlib.pyplot.clf() to clear the current figure (http://matplotlib.org/api/pyplot_api.html#matplotlib.pyplot.clf):

    def plot(self):
        plt.clf()  # Clear the old figure
        im = plt.imshow(self.matrix)
        plt.pause(0.05)
    

    Hope this helps! :)

    0 讨论(0)
提交回复
热议问题