I am writing a program that tracks populations given birth and death rates.
Each cell of the population is a 'dictionary' object that contains information about the cell.
The process is stochastic with exponential arrivals times.
At each arrival, the cell either produces a child or dies. These probabilities are given.
I want to loop over the entire population (cells
) and add to the same list (cells
) over which I am iterating.
So, I used a while True:
loop which only breaks when some time limit (max_t
) is reached.
Additionally, the cells which have died should no longer participate in the simulation so their status
is changed to 'd'
. The loop only takes cells with status == 'l'
.
The issue is the loop goes on forever and I am unable to exit it. Can anyone explain why ?
''' after each birth add copy of parent to cells so it stays in loop'''
import numpy as np
from scipy.stats import bernoulli
def sim_pop(birth, death, init, max_t, max_j):
p = birth/(birth death)
time = [0]
first = {'gen':1, 'parent':None, 'id':'1', 'status':'l', 'path':'1', 'time_born':0, 'time_alive':0, 'time_path':[0]}
cells = [first]
j = init
pop = [j]
flag = True
while flag:
for c in cells:
if c['time_born'] > max_t:
flag = False
break
else:
if c['status'] == 'l':
t = np.random.exponential(1/((birth death)))
time.append(t)
pp = bernoulli.rvs(p)
if pp == 1:
j = j 1
pop.append(j)
n = [i for i in cells if i['parent'] == c['id']] ## number of offpsring of c, siblings of new
ii = str(len(n) 1)
new = {'gen':c['gen'] 1, 'parent':c['id'], 'id':c['id'] '->' ii, 'status':'l', 'path':c['path'] '->' ii, 'time_born':c['time_born'] t,'time_alive':0, 'time_path':c['time_path'] [c['time_born'] t]}
cells.append(new)
## add copy of original so it doesn't get removed from the for loop
copy = c.copy()
copy['time_alive'] = c['time_alive'] t
c['status'] = 'd'
cells.append(copy)
else:
j = j-1
pop.append(j)
c['status'] = 'd' ## change status to dead
c['time_path'] = c['time_path'] [c['time_born'] t] ## add time of death to time path
break
break
times = [i for i in np.cumsum(time)]
alive = [i for i in cells if i['status'] == 'l']
dead = [i for i in cells if i['status'] == 'd']
return times, cells, alive, dead, pop
birth = 0.009
death = 0.003
init = 1
max_t = 1000
max_j = 50000000
times, cells, alive, dead, pop = sim_pop(birth, death, init, max_t, max_j)
print(f'end population = {pop[-1]}')
print(f'cells alive = {len(alive)}')
print(f'cells dead = {len(dead)}') ## remove dupliactes
CodePudding user response:
Remove the break statements at the end of your for loop. Yes, it is typically bad to modify an array you are iterating over, but in this case your issue is you were never getting to the cells with the time_born > max_t
because you broke out of the for loop after the first cell every iteration.
Edit: you actually have another bug. Sometimes all of the cells die but the while loop will continue forever because none of them have a time_born
which exceeds max_t
. I have added a check for this.
''' after each birth add copy of parent to cells so it stays in loop'''
import numpy as np
from scipy.stats import bernoulli
def sim_pop(birth, death, init, max_t, max_j):
p = birth/(birth death)
time = [0]
first = {'gen':1, 'parent':None, 'id':'1', 'status':'l', 'path':'1', 'time_born':0, 'time_alive':0, 'time_path':[0]}
cells = [first]
j = init
pop = [j]
flag = True
while flag:
all_dead = True
new_cells = cells.copy()
for c in cells:
if c['time_born'] > max_t:
flag = False
break
else:
if c['status'] == 'l':
all_dead = False
t = np.random.exponential(1/((birth death)))
time.append(t)
pp = bernoulli.rvs(p)
if pp == 1:
j = j 1
pop.append(j)
n = [i for i in cells if i['parent'] == c['id']] ## number of offpsring of c, siblings of new
ii = str(len(n) 1)
new = {'gen':c['gen'] 1, 'parent':c['id'], 'id':c['id'] '->' ii, 'status':'l', 'path':c['path'] '->' ii, 'time_born':c['time_born'] t,'time_alive':0, 'time_path':c['time_path'] [c['time_born'] t]}
new_cells.append(new)
## add copy of original so it doesn't get removed from the for loop
copy = c.copy()
copy['time_alive'] = c['time_alive'] t
c['status'] = 'd'
new_cells.append(copy)
else:
j = j-1
pop.append(j)
c['status'] = 'd' ## change status to dead
c['time_path'] = c['time_path'] [c['time_born'] t] ## add time of death to time path
cells = new_cells
if all_dead:
break
times = [i for i in np.cumsum(time)]
alive = [i for i in cells if i['status'] == 'l']
dead = [i for i in cells if i['status'] == 'd']
return times, cells, alive, dead, pop
birth = 0.009
death = 0.003
init = 1
max_t = 1000
max_j = 50000000
times, cells, alive, dead, pop = sim_pop(birth, death, init, max_t, max_j)
print(f'end population = {pop[-1]}')
print(f'cells alive = {len(alive)}')
print(f'cells dead = {len(dead)}') ## remove dupliactes
CodePudding user response:
It looks to me that all your break statements are inside for c in cells:
and break this loop, and the while loop keeps going.
You need another break statement that is outside the for c in cells:
loop, or to check your indentation.