SimPy 中的接收器状态

Sink state in SimPy

我正在探索 SimPy 模拟患者在护理过程中的流动。在模拟模型中,患者要么接受治疗,要么死亡。显然,这些都是相互竞争的风险,患者死后不应该接受治疗。但是,我对 SimPy 还很陌生,我还没有发现如何为水槽建模,以便这些已故患者无法继续进行模拟。

我比较熟悉R中的simmer包,里面可以branch()模型结构,指定模型结构的哪个分支是死胡同。 SimPy 中是否有类似的功能?或者还有其他优雅的选择吗?

为了说明我的意思,下面是模拟模型:

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)

这会导致明显不希望的输出:

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.

模拟主要有两种类型 classic 实体流,其中站点拥有所有逻辑,而实体只有很少的逻辑,站点决定实体的命运。 entity/agent 拥有所有代码并决定自己命运的特工基地。

实体流的 classic 示例是制造生产线,其中 stations/machines 决定对实体做什么以及下一步将其发送(分支)到哪里。您的 R 代码听起来像是使用这种范式

你的例子更像是代理,比如 entity/patient 控制自己的行为。

要解决此问题,您只需向 class 添加一个“状态”属性即可跟踪患者是活是死。然后在你的 while 循环中检查病人的状态

见下文:

"""
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)

这是一种更多过程和更少代理基础的编码方式 进程是站

"""
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)