Home > database >  Unable to exit nested while loops
Unable to exit nested while loops

Time:09-03

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.

  • Related