Search code examples
pythonfor-loopoopcombinatoricsmodeling

Modelling a Sheep vs Rabbit population simulation (Python)


I am trying to model a sheep vs rabbit population with the following rules:

  1. If two animals of different species meet on the same square on an nside grid, there is a chance that either none, both or one of them die.
  2. If animals with the same species meet on a square, there is a chance (similar probability as ^^) that a new baby will be born.

In the next time step, the live animals can move either left, right, up or down.

My problem is that in my model, both species are exponentially growing and I'm not sure how to fix this.

What I have tried:
My approach to solving this problem was examine each of the pairs. If the pair doesn't contain a dead animal, then evaluate its status if a new animal should be born or dead using if else conditions. Note that nsides in my code represents the size of the grid. I've also called my animal class People2 as it was adapted from a previous code.

#testing the different if/else functions 

import numpy as np

class People2: #class animals 
    def __init__(self, status, species,  nsides=40): #change here
        self.nsides = nsides
        self.location = np.random.randint(0, self.nsides, [2])
        self.status = status
        self.species=species

    
    def move(self): #moving animals up or down 
        moves = np.random.randint(-1, 2, [2])
        self.location[0] =  (self.location[0]+ moves[0]) % self.nsides
        self.location[1] =  (self.location[1] + moves[1]) % self.nsides


class World2:
    def __init__(self,time=0, maxtime=1000, nsides=40, npeople=2, nprob=0.9):
        self.npeople = npeople
        self.nprob=nprob
        self.nsides=nsides
        self.rabbits=[]
        self.sheep=[]
        #self.people = []
        #who_is_sick = np.random.randint(0, self.npeople)
        self.time=0 
        self.maxtime=1000
        
        for i in range(self.npeople):
            if i%2 == 0:
                self.rabbits.append(People2(status=1, species='R', nsides=nsides))  # sick person 
            else:
                self.sheep.append(People2(status=1, species='S' , nsides=nsides))  # healthy person

        self.people=self.rabbits+self.sheep
        
        
    def movement(self):
        self.to_remove=[]
        self.compare=[]
        new=[]
        
        #pseudocode 
        self.a_list2=list(enumerate(self.people))
        self.anim_combined=list(itertools.combinations(self.a_list2, 2))
        #traverse through list, if I am not my self:
        
        for r, s in self.anim_combined: #note that r,s don't refer to rabits vs sheep, could be anyanimal
            if r[1].status!=0 and s[1].status!=0:
                if np.all(r[1].location==s[1].location): 
                    if (r[1].species!=s[1].species) : #diff species 
                        r_prob, s_prob = np.random.choice([0, 1],p=[self.nprob,1-self.nprob], size=2)
                        if r_prob==1:
                            pass
                        else:
                            r[1].status==0
                        if s_prob==1:
                            pass 
                        else:
                            s[1].status==0

                    else: #same species 
                        if (r[1].species==s[1].species):

                            repro_prob=np.random.choice([1, 0], p=[self.nprob,1-self.nprob] )
                            if repro_prob==0:
                                pass

                            else: #if the prob is 1, then append a "baby"
                                if repro_prob==1:
                                    new.append(People2(status=1, species=r[1].species, nsides=self.nsides)) 

        
        
        
        #remove those who are dead (i.e. status=0) 
        
        unique_elements = list(itertools.chain.from_iterable(self.anim_combined))
        not_killed = [index_and_animal[1] for index_and_animal in unique_elements if index_and_animal[1].status == 1]
        
        # putting all the alive animals back into the original equation
        self.people.extend(new) #adding the new babies 
        self.people.extend(list(set(not_killed))) #adding whatever wasn't killed into this list 
        
        self.time += 1

        for animal in self.people:
            animal.move()

    
        
        return self.people
    

#iterating over 4 days
anim_world = World2()
rabbit_len = []
sheep_len = []

for _ in range(4): #iterating for 10 days
    rabbit_count = 0
    sheep_count = 0

    for animal in anim_world.movement():
        if animal.species == "R":
            rabbit_count += 1
        elif animal.species == "S":
            sheep_count += 1

    rabbit_len.append(rabbit_count)
    sheep_len.append(sheep_count)


If I could see why they are exponentially growing and why some animals arent being killed if they are of different species,that would be great. Thanks in advance!


Solution

  • You are duplicating all the living animals with self.people.extend(list(set(not_killed))). try replacing self.people.extend(new) with self.people = new. That will set it to be all the new ones than you extend all the ones that are still alive. You could alternatively set self.people to a empty array e.g.

    self.people = []
    self.people.extend(new)
    self.people.extend(list(set(not_killed)))
    

    or you can set self.people to list(set(not_killed))

    e.g.

    self.people=list(set(not_killed))
    self.people.extend(new)
    

    You do need to set self.people before you extend it though. otherwise you would remove the extended ones when it is set.

    Hope this helps!

    P.S. slothrop is right you probably want to replace r[1].status==0 with r[1].status=0.