如何与环境交互
在仿真过程中,许多 activity 是以函数的形式作为参数传入的。这些函数可能与环境交互,比如 now
函数用来提取环境当前的时间, get_capacity
函数用于提取环境中 resource
对应的容量, get_n_generated
函数用于获取生成器的状态,或者用 get_mon
函数直接收集的历史监测值。唯一需要注意的是,仿真环境必须要包含在轨迹之中,下面是一个错误示例:
library(simmer) library(simmer.plot) t <- trajectory() %>% log_(function() as.character(now(env))) env <- simmer() %>% add_generator("dummy", t, function() 1) %>% run(4) #> 1: dummy0: #> Error in now(env): object 'env' not found
因为, env
是全局变量,它无法在运行时执行。仿真执行过程于仿真结果的赋值需要分开。在这个仿真用例中,环境 env
由轨迹 t
生成,可以通过 run()
方法将整个过程分离开来:
t <- trajectory() %>% log_(function() as.character(now(env))) env <- simmer() %>% add_generator("dummy", t, function() 1) env %>% run(4) %>% invisible #> 1: dummy0: 1 #> 2: dummy1: 2 #> 3: dummy2: 3
我们获取了预期结果。但是,作为最佳实践的通用规则,还是 建议环境在最初单独初始化
,这样可以避免不必要的错误,也使得代码更具有可读性:
# 首先,初始化环境 env <- simmer() # 生成轨迹 t <- trajectory() %>% log_(function() as.character(now(env))) # 执行环境模拟过程 env %>% add_generator("dummy", t, function() 1) %>% run(4) %>% invisible #> 1: dummy0: 1 #> 2: dummy1: 2 #> 3: dummy2: 3
行动集合
当生成器创建一个到达流的时候,它会给轨迹分配一个到达对象。轨迹在这里的定义是由一个到达对象在系统中全生命周期的一系列行为。一旦一个到达对象被分配到轨迹中,它通常会以一定的顺序开始执行轨迹中的预期行为,最后离开系统。比如:
patient_traj <- trajectory(name = "patient_trajectory") %>% seize(resource = "doctor", amount = 1) %>% timeout(task = 3) %>% release(resource = "doctor", amount = 1)
这里我们创建一个病人就医3分钟然后离开的例子。这是一个直截了当的例子,但是大部分轨迹相关的函数都在此基础上演化高级用法,下面会一一介绍。
此外, 建议你可以尝试下simmer的插件 simmer.bricks
包,它封装了常用的一些轨迹。(见 simmer.bricks入门
)
log_()
log_(., message, level)
方法用来打印仿真过程中的信息以辅助debug,通过不同的 level
可以调整打印的层次:
t <- trajectory() %>% log_("this is always printed") %>% # level = 0 by default log_("this is printed if `log_level>=1`", level = 1) %>% log_("this is printed if `log_level>=2`", level = 2) simmer() %>% add_generator("dummy", t, at(0)) %>% run() %>% invisible #> 0: dummy0: this is always printed simmer(log_level = 1) %>% add_generator("dummy", t, at(0)) %>% run() %>% invisible #> 0: dummy0: this is always printed #> 0: dummy0: this is printed if `log_level>=1` simmer(log_level = Inf) %>% add_generator("dummy", t, at(0)) %>% run() %>% invisible #> 0: dummy0: this is always printed #> 0: dummy0: this is printed if `log_level>=1` #> 0: dummy0: this is printed if `log_level>=2`
set_attribute(), set_global()
set_attribute(., keys, values)
方法提供了设置到达流属性的方法。 keys
和 values
可以以向量或者函数的形式返回。但是, values
只能够以数值型表示。
patient_traj <- trajectory(name = "patient_trajectory") %>% set_attribute(keys = "my_key", values = 123) %>% timeout(5) %>% set_attribute(keys = "my_key", values = 456) env <- simmer() %>% add_generator("patient", patient_traj, at(0), mon = 2) %>% run() get_mon_attributes(env) #> time name key value replication #> 1 0 patient0 my_key 123 1 #> 2 5 patient0 my_key 456 1
如上,轨迹的到达流在 0 时刻(通过 at 函数实现),仅包含 { my_key
:123} 的属性。 add_generator
的 参数 mon = 2
表示对到达流的属性进行持续观察。我们可以用 get_mon_attributes
方法查看 my_key
对应的值在仿真过程中的变化。
如果你想要设置一个存在依赖链路的属性也是允许的。属性可以通过 get_attribute(., keys)
的方式获取。下面是一个实际用例:
patient_traj <- trajectory(name = "patient_trajectory") %>% set_attribute("my_key", 123) %>% timeout(5) %>% set_attribute("my_key", 1, mod="+") %>% timeout(5) %>% set_attribute("dependent_key", function() ifelse(get_attribute(env, "my_key")<=123, 1, 0)) %>% timeout(5) %>% set_attribute("independent_key", function() runif(1)) env<- simmer() %>% add_generator("patient", patient_traj, at(0), mon = 2) env %>% run() #> simmer environment: anonymous | now: 15 | next: #> { Monitor: in memory } #> { Source: patient | monitored: 2 | n_generated: 1 } get_mon_attributes(env) #> time name key value replication #> 1 0 patient0 my_key 123.0000000 1 #> 2 5 patient0 my_key 124.0000000 1 #> 3 10 patient0 dependent_key 0.0000000 1 #> 4 15 patient0 independent_key 0.5500812 1
对于每一次到达,属性只对于到达者可见,其余人不可见。
writer <- trajectory() %>% set_attribute(keys = "my_key", values = 123) reader <- trajectory() %>% log_(function() paste0(get_attribute(env, "my_key"))) env <- simmer() %>% add_generator("writer", writer, at(0), mon = 2) %>% add_generator("reader", reader, at(1), mon = 2) env %>% run() #> 1: reader0: NA #> simmer environment: anonymous | now: 1 | next: #> { Monitor: in memory } #> { Source: writer | monitored: 2 | n_generated: 1 } #> { Source: reader | monitored: 2 | n_generated: 1 } get_mon_attributes(env) #> time name key value replication #> 1 0 writer0 my_key 123 1
因此,在前例中 reader
获取的返回值是缺失值。不过,属性也可以通过 set_global(., keys, values)
全局变量声明:
writer <- trajectory() %>% set_global(keys = "my_key", values = 123) reader <- trajectory() %>% log_(function() paste0(get_attribute(env, "my_key"), ", ", get_global(env, "my_key"))) env <- simmer() %>% add_generator("writer", writer, at(0), mon = 2) %>% add_generator("reader", reader, at(1), mon = 2) env %>% run() #> 1: reader0: NA, 123 #> simmer environment: anonymous | now: 1 | next: #> { Monitor: in memory } #> { Source: writer | monitored: 2 | n_generated: 1 } #> { Source: reader | monitored: 2 | n_generated: 1 } get_mon_attributes(env) #> time name key value replication #> 1 0 my_key 123 1
如上显示,全局变量通过 get_mon_attributes()
赋值未命名的键值对。
参考资料
https://r-simmer.org/articles…
原文作者: Iñaki Ucar, Bart Smeets 译者: Harry Zhu 英文原文地址:
https://r-simmer.org/articles…
作为分享主义者(sharism),本人所有互联网发布的图文均遵从CC版权,转载请保留作者信息并注明作者 Harry Zhu 的 FinanceR专栏: https://segmentfault.com/blog…
,如果涉及源代码请注明GitHub地址: https://github.com/harryprince
。微信号: harryzhustudio
Be First to Comment