Counting neighbours in conway's game of life using python - python

Hello guys I am trying to do conway's game of life as a beginner, however I keep getting IndexError: list index out of range, I am not sure why it happens, can you guys help? thank you!
def count_neighbors(x, y, G):
count= 0
n=len(G)
if x>0:
if y>0:
if G[x-1][y-1]=='x':
count +=1
if y<n-1:
if G[x-1][y+1]=='x':
count +=1
if G[x-1][y]=='x':
count +=1
if y>0:
if G[x][y-1]=='x':
count +=1
if y<n-1:
if G[x][y+1] =='x':
count +=1
if x<n-1:
if y>0:
if G[x+1][y-1] =='x':
count +=1
if y<n-1:
if G[x+1][y+1] =='x':
count +=1
if G[x+1][y]=='x':
count +=1
return count

You're not checking if x < n-1 here, and therefore G[x+1] could be out of bounds:
if y<n-1:
if G[x+1][y+1] =='x':
count +=1
if G[x+1][y]=='x':
count +=1
Upon further inspection, it seems that you should just indent this block once, so that it falls under the if x<n-1: check you wrote above.
Your code is also assuming that G is a square matrix (height == width); if this is not the case, then you will also run into issues.

A more generic answer is to use loops:
def count(x,y,G):
counts = 0
for dy in (-1,0,1):
for dx in (-1,0,1):
if not (dx and dy) and x+dx in range(n) and y+dy in range(n) and G[y+dy][x+dx] == 'x':
counts += 1
return counts
Another trick I've used is to have a list of the possible moves:
dirs = (
(-1,-1), ( 0,-1), ( 1,-1),
(-1, 0), ( 1, 0),
(-1, 1), ( 0, 1), ( 1, 1)
)
def count(x,y,G):
counts = 0
for dx,dy in dirs:
if x+dx in range(n) and y+dy in range(n) and G[y+dy][x+dx] == 'x':
counts += 1
return counts

Update: I just realized I have the wrong rule for 4 neighbors! Fixing...
Update #2: Fixed 2 rules.
I have written numerous programs to play Conway's life game in many languages (including assebly language). None of the programs were easy nor short!
Here is a pure numpy solution to Conway's life game that not only computes all neighbor counts in a few lines of code but also goes on to calculate the next generation in a total of only 9 numpy statements.
import numpy as np
# A 5x5 numpy array represents a 3x3 "world" where the outer edge cells
# will always remain equal to 0.
G = np.array([[0, 0, 0, 0, 0],
[0, 1, 1, 1, 0],
[0, 0, 1, 0, 0],
[0, 1, 0, 1, 0],
[0, 0, 0, 0, 0]])
print('G ---------- Original generation')
print(G)
# Create array S (sum) where each element is the sum of the 3x3 cell surrounding
# the same element in the G array.
S = np.zeros_like(G)
S[1:-1, 1:-1] = sum(G[i:G.shape[0]-2+i, j:G.shape[1]-2+j]
for i in range(0, 3) for j in range(0, 3))
print('S ---------- Provisional neighbor counts')
print(S)
# Since the sums in S include the center cell we subtract the extra values by
# subtracting G from S!
X = np.subtract(S, G)
print('X ---------- Adjusted neighbor counts')
print(X)
# Apply Conway's rules of life
X[((X == 1) | (X > 3)) & (G == 1)] = -1 # Death
X[((X == 1) | (X > 3)) & (G == 0)] = 0 # No change if G is already equal to 0
X[X == 2] = 0 # Survival
X[(X == 3) & (G == 1)] = 0 # No change if G is already equal to 1
X[(X == 3) & (G == 0)] = +1 # Birth otherwise!
print('X ---------- Changes for the next generation')
print(X)
# Apply the calculated changes from X to G
G = np.add(G, X)
print('G ---------- Tada!!! The next generation')
print(G)
Output:
G ---------- Original generation
[[0 0 0 0 0]
[0 1 1 1 0]
[0 0 1 0 0]
[0 1 0 1 0]
[0 0 0 0 0]]
S ---------- Provisional neighbor counts
[[0 0 0 0 0]
[0 3 4 3 0]
[0 4 6 4 0]
[0 2 3 2 0]
[0 0 0 0 0]]
X ---------- Adjusted neighbor counts
[[0 0 0 0 0]
[0 2 3 2 0]
[0 4 5 4 0]
[0 1 3 1 0]
[0 0 0 0 0]]
X ---------- Changes for the next generation
[[ 0 0 0 0 0]
[ 0 0 0 0 0]
[ 0 0 -1 0 0]
[ 0 -1 1 -1 0]
[ 0 0 0 0 0]]
G ---------- Tada!!! The next generation
[[0 0 0 0 0]
[0 1 1 1 0]
[0 0 0 0 0]
[0 0 1 0 0]
[0 0 0 0 0]]

Related

Vectorialize getting the min of elements in a position relative to the current element with numpy

I'm having a hard time vectorializing the following function:
def find_empty_square_sizes(matrix: np.ndarray):
shape = matrix.shape
res_matrix = np.ones(shape, dtype=np.int32)
res_matrix[matrix != 0] = 0
for i in range(1, shape[0]):
for j in range(1, shape[1]):
if matrix[i][j] == 1:
res_matrix[i][j] = 0
else:
res_matrix[i][j] = np.min(np.ma.masked_array(res_matrix[i - 1:i + 1, j - 1:j + 1], mask=[[0, 0], [0, 1]])) + 1
return res_matrix
The idea is to find the biggest square sub matrix of zeros inside a matrix. What the result, res_matrix, means is if the element on row i and column j was the bottom right corner of a sub matrix, how big that sub matrix could be while having only zeros.
Running for example the following code:
m = np.zeros((6, 6), dtype=np.int32)
m[2, 2] = 1
res = find_empty_square_sizes(m)
print(m)
print(res)
Yields the following results:
[[0 0 0 0 0 0]
[0 0 0 0 0 0]
[0 0 1 0 0 0]
[0 0 0 1 0 0]
[0 0 0 0 0 0]
[0 0 0 0 0 0]]
[[1 1 1 1 1 1]
[1 2 2 2 2 2]
[1 2 0 1 2 3]
[1 2 1 0 1 2]
[1 2 2 1 1 2]
[1 2 3 2 2 2]]
I'm doing this for some image processing purposes and for HD images with over a thousand rows and columns this takes about a minute on my PC and I'd like to improve that. Any ideas on how to vectorialize the find_empty_square_sizes function or achieve a similar result through a more efficient method?
If anyone stumbles upon this with a similar problem, following Jerome Richard's advice I used Cython and the following code brought down the execution time from a minute to about 40 ms, quite a difference!
#cython: language_level=3
import cython
import numpy as np
cimport numpy as np
#cython.boundscheck(False)
#cython.wraparound(False)
def find_empty_square_sizes(matrix: np.ndarray) -> np.ndarray:
res_matrix = np.ones((matrix.shape[0], matrix.shape[1]), dtype=np.int32)
res_matrix[matrix != 0] = 0
_find_empty_square_sizes(matrix, res_matrix, matrix.shape[0], matrix.shape[1])
return res_matrix
cdef _find_empty_square_sizes(int[:, :] matrix, int[:, :] res_matrix, int x, int y):
cdef int i
cdef int j
for i in range(1, x):
for j in range(1, y):
if matrix[i][j] == 1:
res_matrix[i][j] = 0
else:
res_matrix[i][j] = _min(res_matrix[i - 1, j - 1], res_matrix[i, j - 1], res_matrix[i - 1, j]) + 1
cdef _min(int a, int b, int c):
cdef int min = a
if b < min:
min = b
if c < min:
min = c
return min

changing the boolean values of an array according to a formula for the indices

I want to create a 64 components array showing all the squares in which the two rooks of an empty chessboard could move from their current position. So far I am doing it with for and while loops.
I first create a function just to better visualize the board:
import numpy as np
def from_array_to_matrix(v):
m=np.zeros((8,8)).astype('int')
for row in range(8):
for column in range(8):
m[row,column]=v[row*8+column]
return m
and here I show how I actually build the array:
# positions of the two rooks
a=np.zeros(64).astype('int')
a[15] = 1
a[25] = 1
print from_array_to_matrix(a)
# attack_a will be all the squares where they could move in the empty board
attack_a=np.zeros(64).astype('int')
for piece in np.where(a)[0]:
j=0
square=piece+j*8
while square<64:
attack_a[square]=1
j+=1
square=piece+j*8
j=0
square=piece-j*8
while square>=0:
attack_a[square]=1
j+=1
square=piece-j*8
j=0
square=piece+j
while square<8*(1+piece//8):
attack_a[square]=1
j+=1
square=piece+j
j=0
square=piece-j
while square>=8*(piece//8):
attack_a[square]=1
j+=1
square=piece-j
print attack_a
print from_array_to_matrix(attack_a)
I have been advised to avoid for and while loops whenever it is possible to use other ways, because they tend to be time consuming. Is there any way to achieve the same result without iterating the process with for and while loops ?
Perhaps using the fact that the indices to which I want to assign the value 1 can be determined by a function.
There are a couple of different ways to do this. The simplest thing is of course to work with matrices.
But you can vectorize operations on the raveled array as well. For example, say you had a rook at position 0 <= n < 64 in the linear array. To set the row to one, use integer division:
array[8 * (n // 8):8 * (n // 8 + 1)] = True
To set the column, use modulo:
array[n % 8::8] = True
You can convert to a matrix using reshape:
matrix = array.reshape(8, 8)
And back using ravel:
array = martix.ravel()
Or reshape:
array = matrix.reshape(-1)
Setting ones in a matrix is even simpler, given a specific row 0 <= m < 8 and column 0 <= n < 8:
matrix[m, :] = matrix[:, n] = True
Now the only question is how to vectorize multiple indices simultaneously. As it happens, you can use a fancy index in one axis. I.e, the expression above can be used with an m and n containing multiple elements:
m, n = np.nonzero(matrix)
matrix[m, :] = matrix[:, n] = True
You could even play games and do this with the array, also using fancy indexing:
n = np.nonzero(array)[0]
r = np.linspace(8 * (n // 8), 8 * (n // 8 + 1), 8, False).T.ravel()
c = np.linspace(n % 8, n % 8 + 64, 8, False)
array[r] = array[c] = True
Using linspace allows you to generate multiple sequences of the same size simultaneously. Each sequence is a column, so we transpose before raveling, although this is not required.
Use reshaping to convert 1-D array to 8x8 2-D matrix and then numpy advance indexing to select rows and columns to set to 1:
import numpy as np
def from_array_to_matrix(v):
return v.reshape(8,8)
# positions of the two rooks
a=np.zeros(64).astype('int')
a[15] = 1
a[25] = 1
a = from_array_to_matrix(a)
# attack_a will be all the squares where they could move in the empty board
attack_a=np.zeros(64).astype('int')
attack_a = from_array_to_matrix(attack_a)
#these two lines replace your for and while loops
attack_a[np.where(a)[0],:] = 1
attack_a[:,np.where(a)[1]] = 1
output:
a:
[[0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 1]
[0 0 0 0 0 0 0 0]
[0 1 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0]]
attack_a:
[[0 1 0 0 0 0 0 1]
[1 1 1 1 1 1 1 1]
[0 1 0 0 0 0 0 1]
[1 1 1 1 1 1 1 1]
[0 1 0 0 0 0 0 1]
[0 1 0 0 0 0 0 1]
[0 1 0 0 0 0 0 1]
[0 1 0 0 0 0 0 1]]

How to find longest consecutive ocurrence of non-zero elements in 2D numpy array

I am simulating protein folding on a 2D grid where every angle is either ±90° or 0°, and have the following problem:
I have an n-by-n numpy array filled with zeros, except for certain places where the value is any integer from 1 to n. Every integer appears just once. Integer k is always a nearest neighbour to k-1 and k + 1, except for the endpoints. The array is saved as an object in the class Grid which I have created for doing energy calculations and folding the protein. Example array, with n=5:
>>> from Grid import Grid
>>> a = Grid(5)
>>> a.show()
[[0 0 0 0 0]
[0 0 0 0 0]
[1 2 3 4 5]
[0 0 0 0 0]
[0 0 0 0 0]]
My goal is to find the longest consecutive line of non-zero elements withouth any bends. In the above case, the result should be 5.
My idea so far are something like this:
def getDiameter(self):
indexes = np.zeros((self.n, 2))
for i in range(1, self.n + 1):
indexes[i - 1] = np.argwhere(self.array == i)[0]
for i in range(self.n):
j = 1
currentDiameter = 1
while indexes[0][i] == indexes[0][i + j] and i + j <= self.n:
currentDiameter += 1
j += 1
while indexes[i][0] == indexes[i + j][0] and i + j <= self.n:
currentDiameter += 1
j += 1
if currentDiameter > diameter:
diameter = currentDiameter
return diameter
This has two problems: (1) it doesn't work, and (2) it is horribly inefficient if I get it to work. I am wondering if anybody has a better way of doing this. If anything is unclear, please let me know.
Edit:
Less trivial example
[[ 0 0 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 10 0 0 0]
[ 0 0 0 0 0 0 9 0 0 0]
[ 0 0 0 0 0 0 8 0 0 0]
[ 0 0 0 4 5 6 7 0 0 0]
[ 0 0 0 3 0 0 0 0 0 0]
[ 0 0 0 2 1 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0 0 0]]
The correct answer here is 4 (both the longest column and the longest row have four non-zero elements).
What I understood from your question is you need to find the length of longest occurance of consecutive elements in numpy array (row by row).
So for this below one, the output should be 5:
[[1 2 3 4 0]
[0 0 0 0 0]
[10 11 12 13 14]
[0 1 2 3 0]
[1 0 0 0 0]]
Because [10 11 12 13 14] are consecutive elements and they have the longest length comparing to any consecutive elements in any other row.
If this is what you are expecting, consider this:
import numpy as np
from itertools import groupby
a = np.array([[1, 2, 3, 4, 0],
[0, 0, 0, 0, 0],
[10, 11, 12, 13, 14],
[0, 1, 2, 3, 0],
[1, 0, 0, 0, 0]])
a = a.astype(float)
a[a == 0] = np.nan
b = np.diff(a) # Calculate the n-th discrete difference. Consecutive numbers will have a difference of 1.
counter = []
for line in b: # for each row.
if 1 in line: # consecutive elements differ by 1.
counter.append(max(sum(1 for _ in g) for k, g in groupby(line) if k == 1) + 1) # find the longest length of consecutive 1's for each row.
print(max(counter)) # find the max of list holding the longest length of consecutive 1's for each row.
# 5
For your particular example:
[[0 0 0 0 0]
[0 0 0 0 0]
[1 2 3 4 5]
[0 0 0 0 0]
[0 0 0 0 0]]
# 5
Start by finding the longest consecutive occurrence in a list:
def find_longest(l):
counter = 0
counters =[]
for i in l:
if i == 0:
counters.append(counter)
counter = 0
else:
counter += 1
counters.append(counter)
return max(counters)
now you can apply this function to each row and each column of the array, and find the maximum:
longest_occurrences = [find_longest(row) for row in a] + [find_longest(col) for col in a.T]
longest_occurrence = max(longest_occurrences)

Python, and The Game of Life Rules

Okay, so i finally get this to print, and actually do something, but the rules are not applying right? I've tried messing with the rules, but can't seem to get them to print out right, here's the snip of my rules:
nCount = 0
for i in range(x-1,x+2):
for j in range(y-1,y+2):
if not(i == x and j == y):
nCount += int(mat[i][j])
if(nCount < 2 or nCount > 3):
return 0
elif(nCount == 2 or nCount == 3):
return 1
else:
return mat[i][j]
I've tested with a regular 5x5 with 3 1's in the middle to act as an oscillator but instead of oscillating it fills the 5x5 edges with zeros like a square box then stops
Here is the rest of my coding:
import time
import os
def buildMatrix(fname):
fp = open(fname, "r")
row = fp.readlines()
matrix = []
for i in range(0, len(row), 1):
token = row[i].split(" ")
token[-1] = token[-1].replace('\n', ' ')
matrix.append(token)
fp.close()
return matrix
def getRows(mat):
return len(mat)
def getCols(mat):
return len(mat[0])
def printGen(mat): #this is what i use for printing
os.system('clear')
for i in range(0, len(mat), 1):
for j in range(0, len(mat[0]), 1):
if(mat[i][j] == 1):
print("#", sep=" ", end=" ")
else:
print(" ", sep=" ", end=" ")
print()
def nextGen(cur, nxt): #use this to update to the next matrix
for i in range(0, len(cur)-1, 1):
for j in range(0, len(cur[0])-1, 1):
nxt[i][j] = neighbors(i, j, cur)
return nxt
def neighbors(x, y, mat):
nCount = 0
for i in range(x-1,x+2):
for j in range(y-1,y+2):
if not(i == x and j == y):
nCount += int(mat[i][j])
if(nCount < 2 or nCount > 3):
return 0
elif(nCount == 2 or nCount ==3):
return 1
else:
return mat[i][j]
This isnt all my code, as im still importing all this to another repo and running it from that repo, but the other part i have done
elif(nCount == 2,3):
This doesn't do what you want. This builds a 2-element tuple whose first element is the value of nCount == 2, and whose second element is 3, then converts this tuple to a boolean to decide which way to go. If you wanted to check whether nCount was equal to either 2 or 3, you could use
elif nCount in (2, 3):
but it's redundant, since nCount must be one of those for control flow to reach the elif. Of course, this isn't correct for implementing the rules of Life; what you want is
elif nCount == 3:
A cell keeps its current live/dead status if surrounded by 2 other live cells. There have to be exactly 3 live cells around it for it to be alive in the next generation regardless of its current life status.
It looks like your code isn't making a copy first, so the results of a birth or death are effecting themselves. Try with a copy:
import numpy
# this function does all the work
def play_life(a):
xmax, ymax = a.shape
b = a.copy() # copy current life grid
for x in range(xmax):
for y in range(ymax):
n = numpy.sum(a[max(x - 1, 0):min(x + 2, xmax), max(y - 1, 0):min(y + 2, ymax)]) - a[x, y]
if a[x, y]:
if n < 2 or n > 3:
b[x, y] = 0 # living cells with <2 or >3 neighbors die
elif n == 3:
b[x, y] = 1 # dead cells with 3 neighbors ar born
return(b)
life = numpy.zeros((5, 5), dtype=numpy.byte)
# place starting conditions here
life[2, 1:4] = 1 # middle three in middle row to 1
# now let's play
print(life)
for i in range(3):
life = play_life(life)
print(life)
I also used numpy for speed. Note that the case for a live cell with 2 or 3 neighbors is taken care of when the copy is made. Here are the results of this test case:
[[0 0 0 0 0]
[0 0 0 0 0]
[0 1 1 1 0]
[0 0 0 0 0]
[0 0 0 0 0]]
[[0 0 0 0 0]
[0 0 1 0 0]
[0 0 1 0 0]
[0 0 1 0 0]
[0 0 0 0 0]]
[[0 0 0 0 0]
[0 0 0 0 0]
[0 1 1 1 0]
[0 0 0 0 0]
[0 0 0 0 0]]
[[0 0 0 0 0]
[0 0 1 0 0]
[0 0 1 0 0]
[0 0 1 0 0]
[0 0 0 0 0]]
Now, if you don't have numpy, then you can use a "2D" array like this:
# this function does all the work
def play_life(a):
xmax = len(a)
ymax = len(a[0])
b = [[cell for cell in row] for row in life]
for x in range(xmax):
for y in range(ymax):
n = 0
for i in range(max(x - 1, 0), min(x + 2, xmax)):
for j in range(max(y - 1, 0), min(y + 2, ymax)):
n += a[i][j]
n -= a[x][y]
if a[x][y]:
if n < 2 or n > 3:
b[x][y] = 0 # living cells with <2 or >3 neighbors die
elif n == 3:
b[x][y] = 1 # dead cells with 3 neighbors ar born
return(b)
# this function just prints the board
def show_life(a):
print('\n'.join([' '.join([str(cell) for cell in row]) for row in life]) + '\n')
# create board
x_size, y_size = (5, 5)
life = [[0 for y in range(y_size)] for x in range(x_size)]
# place starting conditions here
for x in range(1, 4): life[x][2] = 1 # middle three in middle row to 1
# now let's play
show_life(life)
for i in range(3):
life = play_life(life)
show_life(life)
That outputs the following for the test case:
0 0 0 0 0
0 0 1 0 0
0 0 1 0 0
0 0 1 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 1 1 1 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 1 0 0
0 0 1 0 0
0 0 1 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 1 1 1 0
0 0 0 0 0
0 0 0 0 0

Good results when running one by one, wrong when using a loop

I have a 2D grid of ones and zeros. Cluster is defined as nondiagonal set of neighboring ones. For example, if we look at a grid:
[[0 0 0 0 0]
[1 1 1 1 1]
[1 0 0 0 1]
[0 1 0 0 1]
[1 1 1 1 0]]
One cluster would be set of coordinates (actually I use lists for this, but its not important):
c1=[[1, 0], [1, 1], [1, 2], [1, 3], [1, 4], [2, 1], [2, 4], [3, 4]]
The other cluster in this grid is given by:
c2=[[3,1], [4, 0], [4, 1], [4, 2], [4, 3]]
Now, I have made a method that for a given starting coordinate (if it's value is 1), returns a cluster to which that point belongs (for example, if I choose a [1,1] coordinate it would return c1).
For testing I'll choose a point (1, 1) and a small grid. This is the output when result is good:
Number of recursions: 10
Length of cluster: 10
[[1 1 1 0 1]
[1 1 0 1 1]
[0 1 0 0 1]
[1 1 1 0 0]
[0 1 0 1 1]]
[[1 1 1 0 0]
[1 1 0 0 0]
[0 1 0 0 0]
[1 1 1 0 0]
[0 1 0 0 0]]
I was trying to get some idea how fast my algorithm is when cluster size is getting larger. If I run the program and then rerun it, and do that many times, it always gives the good result. If I use a loop, it starts giving wrong results. Here is one possible output test scenario:
Number of recursions: 10
Length of cluster: 10
[[1 1 1 0 1]
[1 1 0 1 1]
[0 1 0 0 1]
[1 1 1 0 0]
[0 1 0 1 1]]
[[1 1 1 0 0]
[1 1 0 0 0]
[0 1 0 0 0]
[1 1 1 0 0]
[0 1 0 0 0]]
Number of recursions: 8
Length of cluster: 8
[[0 1 1 1 0]
[1 1 1 0 0]
[1 0 0 0 0]
[1 1 1 0 1]
[1 1 0 0 0]]
[[0 0 0 0 0] - the first one is always good, this one already has an error
[1 1 0 0 0]
[1 0 0 0 0]
[1 1 1 0 0]
[1 1 0 0 0]]
Number of recursions: 1
Length of cluster: 1
[[1 1 1 1 1]
[0 1 0 1 0]
[0 1 0 0 0]
[0 1 0 0 0]
[0 1 1 0 1]]
[[0 0 0 0 0] - till end
[0 1 0 0 0]
[0 0 0 0 0]
[0 0 0 0 0]
[0 0 0 0 0]]
Number of recursions: 1
Length of cluster: 1
[[1 1 1 1 1]
[0 1 1 0 0]
[1 0 1 1 1]
[1 1 0 1 0]
[0 1 1 1 0]]
[[0 0 0 0 0]
[0 1 0 0 0]
[0 0 0 0 0]
[0 0 0 0 0]
[0 0 0 0 0]]
... till end
I will give the code for loop (it's no problem giving you all code, but it's too big, and the error is probably due to something I do inside a loop):
import numpy as np
from time import time
def test(N, p, testTime, length):
assert N>0
x=1
y=1
a=PercolationGrid(N) #this is a class that creates a grid
a.useFixedProbability(p) #the probability that given point will be 1
a.grid[x,y]=1 #I put the starting point as 1 manually
cluster=Cluster(a)
t0=time()
cluster.getCluster(x,y) #this is what I'm testing how fast is it
t1=time()
stats=cluster.getStats() #get the length of cluster and some other data
testTime.append(t1-t0)
testTime.sort()
length.append(stats[1]) #[1] is the length stat that interests me
length.sort() #both sorts are so I can use plot later
print a.getGrid() #show whole grid
clusterGrid=np.zeros(N*N, dtype='int8').reshape(N, N) #create zero grid where I'll "put" the cluster of interest
c1=cluster.getClusterCoordinates() #this is recursive method (if it has any importance)
for xy in c1:
k=xy[0]
m=xy[1]
clusterGrid[k, m]=1
print clusterGrid
del a, cluster, clusterGrid
testTime=[]
length=[]
p=0.59
N=35
np.set_printoptions(threshold='nan') #so the output doesn't shrink
for i in range(10):
test(N, p, testTime, length)
I assume that I do something wrong with freeing memory or something (if it's not some trivial error in loop I can't see)? I use python 2.7.3 on 64bit Linux.
EDIT:
I'm aware that people here should not review whole codes, but specific problems, but I can't find what's happening, the only suggestion is that maybe I have some static variables, but it seems to me that that is not the case. So, if someone has good will and energy you can browse through a code and maybe you'll see something. I started using classes not while ago, so be prepared for lot of bad stuff.
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
import time
class ProbabilityGrid(object):
"""
This class gives 2D quadratic array (a grid) which is filled with
float values from 0-1, which in many cases represent probabilities
"""
def __init__(self, size=2, dataType='float16'):
"""initialization of a grid with 0. values"""
assert size>1
assert dataType=='float64' or dataType=='float32' or dataType=='float16'
self.n=size
self.dataType=dataType
self.grid=np.zeros((size, size), dtype=dataType)
def getGrid(self):
"""returns a 2D probability array"""
return self.grid
def getSize(self):
"""returns a size of a 2D array"""
return self.size
def fillRandom(self):
"""fills the grid with uniformly random values from 0 to 1"""
n=self.n
self.grid=np.random.rand(n, n)
def fixedProbabilities(self, p):
"""fills the grid with fixed value from 0 to 1"""
assert p<1.0
self.grid=p*np.ones((self.n, self.n))
class PercolationGrid(object):
"""
percolation quadratic grid filled with 1 and 0, int8
which represent a state.
Percolation grid is closly connected to probabilies grid.
ProbabilityGrid gives the starting probabilities will the [i,j] spot
be filled or not. All functions change the PercolationGrid.grid when
ProbabilityGrid.grid changes, so in a way their values are connected
"""
def __init__(self, size=2, dataType='int8'):
"""
initialization of PercolationGrid, sets uniformly 0 and 1 to grid
"""
assert size>1
assert dataType=='int64' or dataType=='int32' or dataType=='int8'
self.n=size
self.dataType=dataType
self.grid=np.zeros((size, size), dtype=dataType)
self.pGrid=ProbabilityGrid(self.n)
self.pGrid.fillRandom()
self.useProbabilityGrid()
#def fillRandom(self, min=0, max=1, distribution='uniform'):
# n=self.n
# self.grid=np.random.random_integers(min, max, n*n).reshape(n, n)
def getGrid(self):
"""returns a 2D percolation array"""
return self.grid
def useProbabilityGrid(self): #use probability grid to get Percolation grid of 0s and 1es
"""
this method fills the PercolationGrid.grid according to probabilities
from Probability.grid
"""
comparisonGrid=np.random.rand(self.n, self.n)
self.grid=np.array(np.floor(self.pGrid.grid-comparisonGrid)+1, dtype=self.dataType)
# Here I used a trick. To simulate whether 1 will apear with probability p,
# we can use uniform random generator which returns values from 0 to 1. If
# the value<p then we get 1, if value>p it's 0.
# But instead looping over each element, it's much faster to make same sized
# grid of random, uniform values from 0 to 1, calculate the difference, add 1
# and use floor function which round everything larger than 1 to 1, and lower
# to 0. Then value-p+1 will give 0 if value<p, 1 if value>p. The result is
# converted to data type of percolation array.
def useFixedProbability(self, p):
"""
this method fills the PercolationGrid according to fixed probabilities
of being filled, for example, a large grid with parameter p set to 0.33
should, aproximatly have one third of places filed with ones and 2/3 with 0
"""
self.pGrid.fixedProbabilities(p)
self.useProbabilityGrid()
def probabilityCheck(self):
""" this method checks the number of ones vs number of elements,
good for checking if the filling of a grid was close to probability
we had in mind. Of course, the accuracy is larger as grid size grows.
For smaller grid sizes you can still check the probability by
running the test multiple times.
"""
sum=self.grid.sum()
print float(sum)/float(self.n*self.n)
#this works because values can only be 0 or 1, so the sum/size gives
#the ratio of ones vs size
def setGrid(self, grid):
shape=grid.shape
i,j=shape[0], shape[1]
assert i>1 and j>1
if i!=j:
print ("The grid needs to be NxN shape, N>1")
self.grid=grid
def setProbabilities(self, grid):
shape=grid.shape
i,j=shape[0], shape[1]
assert i>1 and j>1
if i!=j:
print ("The grid needs to be NxN shape, N>1")
self.pGrid.grid=grid
self.useProbabilityGrid()
def showPercolations(self):
fig1=plt.figure()
fig2=plt.figure()
ax1=fig1.add_subplot(111)
ax2=fig2.add_subplot(111)
myColors=[(1.0, 1.0, 1.0, 1.0), (1.0, 0.0, 0.0, 1.0)]
mycmap=mpl.colors.ListedColormap(myColors)
subplt1=ax1.matshow(self.pGrid.grid, cmap='jet')
cbar1=fig1.colorbar(subplt1)
subplt2=ax2.matshow(self.grid, cmap=mycmap)
cbar2=fig2.colorbar(subplt2, ticks=[0.25,0.75])
cbar2.ax.set_yticklabels(['None', 'Percolated'], rotation='vertical')
class Cluster(object):
"""This is a class of percolation clusters"""
def __init__(self, array):
self.grid=array.getGrid()
self.N=len(self.grid[0,])
self.cluster={}
self.numOfSteps=0
#next 4 functions return True if field next to given field is 1 or False if it's 0
def moveLeft(self, i, j):
moveLeft=False
assert i<self.N
assert j<self.N
if j>0 and self.grid[i, j-1]==1:
moveLeft=True
return moveLeft
def moveRight(self, i, j):
moveRight=False
assert i<self.N
assert j<self.N
if j<N-1 and self.grid[i, j+1]==1:
moveRight=True
return moveRight
def moveDown(self, i, j):
moveDown=False
assert i<self.N
assert j<self.N
if i<N-1 and self.grid[i+1, j]==1:
moveDown=True
return moveDown
def moveUp(self, i, j):
moveUp=False
assert i<self.N
assert j<self.N
if i>0 and self.grid[i-1, j]==1:
moveUp=True
return moveUp
def listOfOnes(self):
"""nested list of connected ones in each row"""
outlist=[]
for i in xrange(self.N):
outlist.append([])
helplist=[]
for j in xrange(self.N):
if self.grid[i, j]==0:
if (j>0 and self.grid[i, j-1]==0) or (j==0 and self.grid[i, j]==0):
continue # condition needed because of edges
outlist[i].append(helplist)
helplist=[]
continue
helplist.append((i, j))
if self.grid[i, j]==1 and j==self.N-1:
outlist[i].append(helplist)
return outlist
def getCluster(self, i=0, j=0, moveD=[1, 1, 1, 1]):
#(left, right, up, down)
#moveD short for moveDirections, 1 means that it tries to move it to that side, 0 so it doesn't try
self.numOfSteps=self.numOfSteps+1
if self.grid[i, j]==1:
self.cluster[(i, j)]=True
else:
print "the starting coordinate is not in any cluster"
return
if moveD[0]==1:
try: #if it comes to same point from different directions we'd get an infinite recursion, checking if it already been on that point prevents that
self.cluster[(i, j-1)]
moveD[0]=0
except:
if self.moveLeft(i, j)==False: #check if 0 or 1 is left to (i, j)
moveD[0]=0
else:
self.getCluster(i, j-1, [1, 0, 1, 1]) #right is 0, because we came from left
if moveD[1]==1:
try:
self.cluster[(i, j+1)]
moveD[1]=0
except:
if self.moveRight(i, j)==False:
moveD[1]=0
else:
self.getCluster(i, j+1, [0, 1, 1, 1])
if moveD[2]==1:
try:
self.cluster[(i-1, j)]
moveD[2]=0
except:
if self.moveUp(i, j)==False:
moveD[2]=0
else:
self.getCluster(i-1, j, [1, 1, 1, 0])
if moveD[3]==1:
try:
self.cluster[(i+1, j)]
moveD[3]=0
except:
if self.moveDown(i, j)==False:
moveD[3]=0
else:
self.getCluster(i+1, j, [1, 1, 0, 1])
if moveD==(0, 0, 0, 0):
return
def getClusterCoordinates(self):
return self.cluster
def getStats(self):
print "Number of recursions:", self.numOfSteps
print "Length of cluster:", len(self.cluster)
return (self.numOfSteps, len(self.cluster))
Your error is coming from the getCluster method. When setting moveD to [1,1,1,1] you are essentially setting a static variable(do not quote me on this). This is causing the information from the previous executions to carry over.
Here is a link to a blog post that shows an example of this.
Below is a working version of the getCluster method that both fixes the default arguement problem and removes the extraneous moveD assignments that manifested the problematic behavior.
def getCluster(self, i=0, j=0, moveD=None):
#(left, right, up, down)
#moveD short for moveDirections, 1 means that it tries to move it to that side, 0 so it doesn't try
if moveD == None: moveD = [1, 1, 1, 1]
self.numOfSteps=self.numOfSteps+1
if self.grid[i, j]==1:
self.cluster[(i, j)]=True
else:
print "the starting coordinate is not in any cluster"
return
if moveD[0]==1:
try: #if it comes to same point from different directions we'd get an infinite recursion, checking if it already been on that point prevents that
self.cluster[(i, j-1)]
except:
if self.moveLeft(i, j)==True: #check if 0 or 1 is left to (i, j)
self.getCluster(i, j-1, [1, 0, 1, 1]) #right is 0, because we came from left
if moveD[1]==1:
try:
self.cluster[(i, j+1)]
except:
if self.moveRight(i, j)==True:
self.getCluster(i, j+1, [0, 1, 1, 1])
if moveD[2]==1:
try:
self.cluster[(i-1, j)]
except:
if self.moveUp(i, j)==True:
self.getCluster(i-1, j, [1, 1, 1, 0])
if moveD[3]==1:
try:
self.cluster[(i+1, j)]
except:
if self.moveDown(i, j)==True:
self.getCluster(i+1, j, [1, 1, 0, 1])

Categories

Resources