mivandev
mivandev

Reputation: 321

Sink state in SimPy

I am exploring SimPy to model patient flows through care processes. In a mock model, patients can either receive treatment or die. Obviously, these are competing risks, and patients should not be able to receive treatment after dying. However, I am very new to SimPy and I have yet to discover how to model a sink so that these deceased patients cannot continue through the simulation.

I am more familiar with the simmer package in R, in which you can branch() the model structure and specify which branch of the model structure is a dead end. Is there similar functionality in SimPy? Or are there other elegant options?

To illustrate what I mean, below is the mock model:

from random import seed, randint
seed(123)

MAX_CYCLES = 5
PROB_DEATH = 0.2

class Patient:
    def __init__(self, env, treatment_cycles = 0):
        self.env = env
        self.treatment_cycles = treatment_cycles
        self.treatment_proc = env.process(self.get_treatment(env))
                
    def get_treatment(self, env):
        while self.treatment_cycles < MAX_CYCLES:
            self.treatment_cycles += 1

            rand = random.uniform(0, 1)           
            if (rand < PROB_DEATH):
                yield env.timeout(random.randint(20, 40))
                print('I have died at {}'.format(env.now))
                ### Once this point has been reached, patients should not be able to continue ###
            else:
                yield env.timeout(randint(20, 80))
                print("I have completed a full cycle of treatment after {} days.".format(int(env.now)))

        
env = simpy.Environment()
pat = Patient(env)
env.run(until=250)

This results in obviously undesired output:

I have died at 22
I have completed a full cycle of treatment after 59 days.
I have died at 80
I have completed a full cycle of treatment after 135 days.
I have completed a full cycle of treatment after 209 days.

Upvotes: 0

Views: 202

Answers (2)

Michael
Michael

Reputation: 1969

here is a more process and less agent base way to code it where the processes are the stations

"""
Show how to use a state variable to change behavior
less agent, more process

programmer: Michael R. Gibbs
"""
from random import seed, randint, uniform
import simpy

seed(123)

MAX_CYCLES = 5
PROB_DEATH = 0.2

def died(env, patient):
    """
    patien dies, and no more processing
    """

    yield env.timeout(randint(20, 80))
    patient.sate = 'Dead'
    print(f"Patient {patient.id} has died on day {env.now}")

def provide_treatment(env, patient):
    """
    determine fate of patient
    if lives provide treatment and send to next treatment
    if dies, send to death process
    """

    if patient.treatment_cycles < MAX_CYCLES:
        patient.treatment_cycles += 1

        rand = uniform(0, 1)           
        if (rand < PROB_DEATH):
            # patient has died
            env.process(died(env,patient))

        else:
            # patient lives
            yield env.timeout(randint(20, 80))
            print(f"Patient {patient.id} completed a full cycle of treatment after {env.now} days.")
            
            # send to next treatment
            env.process(provide_treatment(env,patient))
    else:
        # done with all treatments, do not send anywhere
        print(f"Patient {patient.id} has completed all treatments after {env.now} days")



class Patient:
    """
    patient seeking treatment
    """
    next_id = 1

    def __init__(self, env, treatment_cycles = 0):

        self.state = 'Live' # starts out as a living patient

        self.id = Patient.next_id
        Patient.next_id += 1

        self.env = env
        self.treatment_cycles = treatment_cycles
                
        
env = simpy.Environment()
pat = Patient(env)
env.process(provide_treatment(env,pat))
pat = Patient(env)
env.process(provide_treatment(env,pat))
pat = Patient(env)
env.process(provide_treatment(env,pat))
pat = Patient(env)
env.process(provide_treatment(env,pat))
env.run(until=250)

Upvotes: 0

Michael
Michael

Reputation: 1969

There are two main types of simulation The classic entity flow where stations have all the logic and the entity has very little and the stations decide the fate of the entity. Agent base where the entity/agent has all the code and decides it's own fate.

The classic example for entity flow is manufacturing production lines where the stations/machines decide what to do to the entity and where to send it (branch to) next. Your R code sounds like it uses this paradigm

Your example is more agent like where the entity/patient controls its own behavior.

To fix this you just need to add a "state" attribute to the class to track if the patient is live or dead. Then check the patient's state in your while loop

see below:

"""
Show how to use a state variable to change behavior

programmer: Michael R. Gibbs
"""
from random import seed, randint, uniform
import simpy

seed(123)

MAX_CYCLES = 5
PROB_DEATH = 0.2

class Patient:
    """
    patient seeking treatment
    """
    def __init__(self, env, treatment_cycles = 0):
        self.state = 'Live' # starts out as a living patient

        self.env = env
        self.treatment_cycles = treatment_cycles
        self.treatment_proc = env.process(self.get_treatment(env))
                
    def get_treatment(self, env):
        """
        gets a treatment that can change the patient's
        state from live, to dead
        """

        while self.treatment_cycles < MAX_CYCLES and self.state=='Live':
            # treat untile done, or patient has died

            self.treatment_cycles += 1

            rand = uniform(0, 1)           
            if (rand < PROB_DEATH):
                # patient has died
                yield env.timeout(randint(20, 40))
                print('I have died at {}'.format(env.now))

                # update state to dead
                self.state = 'Dead'
                ### Once this point has been reached, patients should not be able to continue ###
            else:
                yield env.timeout(randint(20, 80))
                print("I have completed a full cycle of treatment after {} days.".format(int(env.now)))

        
env = simpy.Environment()
pat = Patient(env)
env.run(until=250)

Upvotes: 2

Related Questions