Python - Simpy 4.0.1 - 环境中的多种输入

Python - Simpy 4.0.1 - multiple kinds of inputs in an Environment

我一直在解决一个问题,我必须在 Simpy 4.0.1 程序中模拟治疗中心的患者,该程序有多个类别的患者(比如“A”、“B”和“C”) .患者根据 numpy.random.exponential() 产生的超时“到达”,并根据 numpy.random.lognormal() 在中心“占床”(Simpy 资源)。每种患者类型都有一组不同的值来控制此采样。这是一个简单的 FIFO 过程,不涉及优先级,患者只是以不同的速度到达和停留。

我尝试的第一种方法是为每一类患者创建 3 个进程,并让 Simpy 处理它们的生成:

import simpy
from numpy.random import exponential, lognormal

counter = 0
total_beds = 5
run_length = 10

mean_iat_a, mean_iat_b, mean_iat_c = 1.0, 2.0, 3.0
normal_mean_a, normal_mean_b, normal_mean_c = 4.0, 5.0, 6.0
normal_stdev_a, normal_stdev_b, normal_stdev_c = 7.0, 8.0, 9.0


class Patient:
    def __init__(self, pid, ptype):
        self.pid = pid
        self.ptype = ptype
        self.time_arrived = None
        self.time_admitted = None
        self.time_discharged = None

def generate_arrivals(env, ptype):
    while True:
        global counter
        counter += 1
        patient = Patient(counter, ptype)
        env.process(perform_treatment(env, patient))
        # sample arrivals
        if patient.ptype == 'A':
            interarival_time = exponential(scale=mean_iat_a)
        elif patient.ptype == 'B':
            interarival_time = exponential(scale=mean_iat_b)
        else:
            interarival_time = exponential(scale=mean_iat_c)
        yield env.timeout(interarival_time)


def perform_treatment(env, patient):
    patient.time_arrived = env.now
    print(f'Patient {patient.pid} - {patient.ptype} - arrived at {patient.time_arrived}')
    with beds.request() as req:
        yield req
        patient.time_admitted = env.now
        print(f'Patient {patient.pid} - {patient.ptype} - admitted at {patient.time_admitted}')
        # sample duration of stay
        if patient.ptype == 'A':
            stay_duration = lognormal(mean=normal_mean_a, sigma=normal_stdev_a)
        elif patient.ptype == 'B':
            stay_duration = lognormal(mean=normal_mean_b, sigma=normal_stdev_b)
        else:
            stay_duration = lognormal(mean=normal_mean_c, sigma=normal_stdev_c)
        yield env.timeout(stay_duration)
        patient.time_discharged = env.now
        print(f'Patient {patient.pid} - {patient.ptype} - discharged at {patient.time_discharged}')


env = simpy.Environment()
beds = simpy.Resource(env, capacity=total_beds)
env.process(generate_arrivals(env, 'A'))
env.process(generate_arrivals(env, 'B'))
env.process(generate_arrivals(env, 'C'))
env.run(until=run_length)

这种方法的问题在于,每个类别的患者在其各自流程开始时都会创建(它总是在时间 = 0 时创建每个类别的患者 1、2 和 3),但它不会反映真实场景:

Patient 1 - A - arrived at 0
Patient 2 - B - arrived at 0
Patient 3 - C - arrived at 0
Patient 1 - A - admitted at 0
Patient 2 - B - admitted at 0
Patient 3 - C - admitted at 0
Patient 1 - A - discharged at 0.04165029350880402
Patient 4 - A - arrived at 0.6503311494436321
Patient 4 - A - admitted at 0.6503311494436321
Patient 4 - A - discharged at 0.6626671650563922
Patient 5 - B - arrived at 0.6868621026906724
.
.
etc

到目前为止,我一直在考虑使用:

  1. child 类 存储类别特定常量和
  2. 通过 numpy.random.multinomial() 以相同的概率随机决定生成哪种患者的单个过程。
import simpy
from numpy.random import exponential, lognormal, multinomial


counter = 0
total_beds = 5
run_length = 10
ptypes = ['A', 'B', 'C']
probvals = [1.0 / len(ptypes) for _ in ptypes]


class Patient:
    def __init__(self, pid, ptype):
        self.pid = pid
        self.ptype = ptype
        self.time_arrived = None
        self.time_admitted = None
        self.time_discharged = None


class TypeA(Patient):
    def __init__(self, pid, ptype):
        super().__init__(pid, ptype)
        self.mean_iat = 1.0
        self.normal_mean = 4.0
        self.normal_stdev = 7.0


class TypeB(Patient):
    def __init__(self, pid, ptype):
        super().__init__(pid, ptype)
        self.mean_iat = 2.0
        self.normal_mean = 5.0
        self.normal_stdev = 8.0


class TypeC(Patient):
    def __init__(self, pid, ptype):
        super().__init__(pid, ptype)
        self.mean_iat = 3.0
        self.normal_mean = 6.0
        self.normal_stdev = 9.0


def generate_arrivals(env):
    while True:
        global counter
        counter += 1
        ptype = ptypes[multinomial(n=1, pvals=probvals).argmax()]
        
        # From here is where I'm stuck

        # Can't understand how to drive the correct object instantiation
        # to replace the if-elif-else from the previous example
        patient = Patient(counter, ptype)

        # Not sure of the logic from here on out
        env.process(perform_treatment(env, patient))
        interarival_time = exponential(scale=patient.mean_iat)
        yield env.timeout(interarival_time)


def perform_treatment(env, patient):
    patient.time_arrived = env.now
    print(f'Patient {patient.pid} - {patient.ptype} - arrived at {patient.time_arrived}')
    with beds.request() as req:
        yield req
        patient.time_admitted = env.now
        print(f'Patient {patient.pid} - {patient.ptype} - admitted at {patient.time_admitted}')
        
        # Again, how do I replace the if-elif-else from the previous example
        stay_duration = lognormal(mean=patient.normal_mean, sigma=patient.normal_stdev)
        yield env.timeout(stay_duration)
        patient.time_discharged = env.now
        print(f'Patient {patient.pid} - {patient.ptype} - discharged at {patient.time_discharged}')


env = simpy.Environment()
beds = simpy.Resource(env, capacity=total_beds)
env.process(generate_arrivals(env))
env.run(until=run_length)

但是,我似乎无法理解如何使用 Simpy 来完成它。对我的方法或替代想法的任何意见将不胜感激!

注意: 我知道使用 numpy.random.multinomial() 会导致一次只生成一种患者,但这可能完全是一个不同的问题。

在你的第一个代码中,你能否移动你的 gen patient 代码,你能否将你的超时移到循环的顶部。当然这意味着你不会在时间 0

得到任何病人

您在 env.run 之前启动的每个进程都在时间 0 开始,并且在第一个 yield 之前发生的所有事情都在时间 0 发生。调用 env.timeout 不会影响其他进程。我想到了每个进程和它自己的线程。所以当你的程序调用env.run时。它抓住你的一个进程并创建一个病人,然后在 yield env 等待。 timeout(),然后它会抓取第二个进程,仍然在时间 0,并创建一个患者并在 yield 处暂停。等...等...在短时间内不会在过程中移动,直到它达到产量。这有帮助吗? – 迈克尔 11 小时前 删除 当流程中的第一行是 yield env.timeout() 时,在超时完成并且时间已更新之前什么都不会发生。它具有在进程应该启动时进行调度的效果