patternpythonMinor
A big "Game of Life"
Viewed 0 times
lifebiggame
Problem
Our quest: Create a big simulation for Conway's Game of Life, and record the entire simulation history.
Current Approach: Cython is used for an
What I could use help with: While I'd love advice on anything, I'm especially interested in improving the pandas implementation in this code. Also, how could I make this file easier for others to read?
Explanation of definitions
iterate.pyx
```
#cython: wraparound=False, boundscheck=False, cdivision=True
#cython: profile=False, nonecheck=False, overflowcheck=False
#cython: cdivision_warnings=False, unraisable_tracebacks=False
import numpy as np
cimport numpy as np
cpdef iterate(Z, c):
'''Element by elemenent iteration with optimized Cython.
Args:
Z (ndarray - int32) - Represents 2D space
c (namedtuple) - Container for constants
Returns:
Z (ndarray - int32)
'''
N = np.zeros((c.rows-1, c.cols-1), dtype=np.int32)
cdef int rows = c.rows
cdef int cols = c.cols
cdef int [:, :] N_ = N
cdef int [:, :] Z_ = Z
cdef int x, y
with nogil:
# Count neighbors
for x in range(1, rows-1):
for y in range(1, cols-1):
N_[x, y] = (Z_[x-1, y-1] + Z_[x-1, y] + Z_[x-1, y+1] +
Z_[x, y-1] + Z_[x, y+1] +
Z_[x+1, y-1] + Z_[x+1, y] + Z_[x+1, y+1])
# Apply rules
for x in range(1, rows-1):
for y in range(1, cols-1):
if Z_[x, y] == 1 and (N_[x, y] 3):
Z_[x, y] = 0
elif Z_[x, y] == 0 and N_[x, y]
Current Approach: Cython is used for an
iterate method. The history of life is placed in an HDF store, with help from pandas, and matplotlib is used to visually check results.What I could use help with: While I'd love advice on anything, I'm especially interested in improving the pandas implementation in this code. Also, how could I make this file easier for others to read?
Explanation of definitions
- Z (ndarray, int32): 2D array of life (1 or 0).
- N (ndarray, int32): Temporary array used to tally the count of neighbors at each point on Z. N has the shape of Z minus one on each axis.
- Z_chunk (DataFrame, int32): History of the past few iterations of Z
iterate.pyx
```
#cython: wraparound=False, boundscheck=False, cdivision=True
#cython: profile=False, nonecheck=False, overflowcheck=False
#cython: cdivision_warnings=False, unraisable_tracebacks=False
import numpy as np
cimport numpy as np
cpdef iterate(Z, c):
'''Element by elemenent iteration with optimized Cython.
Args:
Z (ndarray - int32) - Represents 2D space
c (namedtuple) - Container for constants
Returns:
Z (ndarray - int32)
'''
N = np.zeros((c.rows-1, c.cols-1), dtype=np.int32)
cdef int rows = c.rows
cdef int cols = c.cols
cdef int [:, :] N_ = N
cdef int [:, :] Z_ = Z
cdef int x, y
with nogil:
# Count neighbors
for x in range(1, rows-1):
for y in range(1, cols-1):
N_[x, y] = (Z_[x-1, y-1] + Z_[x-1, y] + Z_[x-1, y+1] +
Z_[x, y-1] + Z_[x, y+1] +
Z_[x+1, y-1] + Z_[x+1, y] + Z_[x+1, y+1])
# Apply rules
for x in range(1, rows-1):
for y in range(1, cols-1):
if Z_[x, y] == 1 and (N_[x, y] 3):
Z_[x, y] = 0
elif Z_[x, y] == 0 and N_[x, y]
Solution
DISCLAIMER: I don't know cython and have never used it, so if any of my advice doesn't apply because of cython limitations, feel free to disregard it.
-
Why do you need pandas at all? Are you using it just to store HDF5 files? If so, I'd recommend looking at h5py instead. You may have to learn a bit more about how HDF5 file formats work, but I think it would be more efficient than Pandas. In particular, in an application like this where the game board is naturally a matrix, Pandas'
-
Probably defining your own chunking will improve HDF5 performance at the largest sizes, but it depends on how you want to view the data. Do you want to view the entire board for a single iteration easily? Or do you want to view a small region of the board across multiple iterations easily? Unless you have particular views in mind, a good starting place would be to just use
-
This is minor, but why are you using any interpolation at all in
- Counting neighbors in a game board is very easy to do via convolution with the proper kernel. Below, I used SciPy's
convolvefunction, not the much fasterfftconvolve, because the latter only works on floats and so rounding back to integers / bools would be required. Nonetheless, I suspect for large game boards, the FFT method and rounding may still be faster. I doubt that Cython would speed up NumPy very much, but I would love to be proven wrong! (A Cython guide for NumPy users says Typical Python numerical programs would tend to gain very little as most time is spent in lower-level C that is used in a high-level fashion.) Anyway, to my eye at least this function is a bit easier to read and interpret.
import numpy as np
from scipy.signal import convolve
def iterate_game_of_life(board, neighbors_kernel=None):
"""Performs one iteration of Conway's game of life on a 2d numpy array of bools"""
if neighbors_kernel is None:
neighbors_kernel = np.ones(shape=(3, 3), dtype='int')
neighbors_kernel[1, 1] = 0
neighbors_count = convolve(board, neighbors_kernel, mode='same')
has_three, has_two = neighbors_count == 3, neighbors_count == 2
return np.logical_or(has_three, np.logical_and(board, has_two))-
Why do you need pandas at all? Are you using it just to store HDF5 files? If so, I'd recommend looking at h5py instead. You may have to learn a bit more about how HDF5 file formats work, but I think it would be more efficient than Pandas. In particular, in an application like this where the game board is naturally a matrix, Pandas'
DataFrame model that rows and columns need to have labels seems like annoying overhead instead of a feature. You could even store the iterations of the game as a single datacube instead of as separate HDF5 datasets.-
Probably defining your own chunking will improve HDF5 performance at the largest sizes, but it depends on how you want to view the data. Do you want to view the entire board for a single iteration easily? Or do you want to view a small region of the board across multiple iterations easily? Unless you have particular views in mind, a good starting place would be to just use
h5py's autochunking, i.e., don't define your own.-
This is minor, but why are you using any interpolation at all in
ax.imshow(Z, interpolation='nearest', cmap=plt.cm.gray_r)? I'd recommend ax.imshow(Z, interpolation='none', cmap=plt.cm.gray_r).Code Snippets
import numpy as np
from scipy.signal import convolve
def iterate_game_of_life(board, neighbors_kernel=None):
"""Performs one iteration of Conway's game of life on a 2d numpy array of bools"""
if neighbors_kernel is None:
neighbors_kernel = np.ones(shape=(3, 3), dtype='int')
neighbors_kernel[1, 1] = 0
neighbors_count = convolve(board, neighbors_kernel, mode='same')
has_three, has_two = neighbors_count == 3, neighbors_count == 2
return np.logical_or(has_three, np.logical_and(board, has_two))Context
StackExchange Code Review Q#100642, answer score: 6
Revisions (0)
No revisions yet.