1 DEVS --离散事件运行规范
离散事件系统规范 DEVS( discrete event system specification)是由美国学者 B. P. Zeigler等提出的一种模块化建模方法 ,它将描述对象中的每个子系统看作一个具有内部独立结构和输入输出接口 的模块,具有规范化、层次化、模块化的建模优势。
DEVS是一种离散事件运行规范,专门用于处理离散事件的仿真,例如大学学过的狼羊农夫过河问题、元胞自动机问题等,都可以划分到这个范围里。
DEVS不是一门语言,更像是一种带有完整理论和仿真执行机制的建模思想,编程语言是其实现的载体。这一点有些像Torch这个深度学习框架,重要的是这一套前向反向传播的机制流程和思路,而不是基于python还是c++实现Torch架构。
DEVS包含模型、仿真器、实验框架三个部分,和建模过程最相关的就是模型。模型分为原子模型和耦合模型,耦合模型是原子模型之间互相连接得到的。
最重要的原子模型的定义为 :
M = < X , S , Y , δ i n t , δ e x t , λ , t a > M = <X, S,Y,\delta_{int},\delta_{ext},\lambda,t_a> M=<X,S,Y,δint,δext,λ,ta>
其中 X 是外部输入集 , S是系统的序列状态集 , Y是外部输出集 ;
δ
i
n
t
\delta_{int}
δint 是内部转移函数 , 它描述内部事件发生所引起的系统状态变化 ;
δ
e
x
t
\delta_{ext}
δext是外部转移函数 , 描述的是外部事件引起的系统状态变化;
λ
\lambda
λ是输出函数 , 它为输出端产生外部事件:
t
a
t_a
ta是时间推进函数,用于产生每一个状态的持续时间。
DEVS为描述模型提供了一种严格的规范 ,它为建立模型的边界层次和抽象模型之间的依赖关系提供了一 种方法。 因而 , 能够很方便的描述智能体的决策和适应过程 , 以及它们之间的交互 。
2 python下安装devs包
python的官网有一个名为devs 0.1.8的包,并且说只需要pip就可以安装。但是实际上这个包已经很久没有更新过了,基本上安装的时候会出很多的问题,所以不建议使用官网的devs包。
github上有一些python的devs包,都是国外各个大学或者研究机构的仿真大佬们开发的,在这里推荐一个非常好用的库:pythonPDEVS。
如果下载不下来的话,可以使用网盘资源(提取码:kyrq)。
将整个文件夹下载下来后,找到里面的src文件夹,在当前路径下打开CMD命令行窗口,输入:
python setup.py install --user
安装完成后,打开一个python脚本文件,输入:
import pypdevs
如果import成功代表安装完成。
3 pypdevs包的宏观解读
pypdevs库的全部说明在官方文档里写的比较清晰。
总的来说: 作为一个基于面向对象架构的DEVS包,pypdevs有三个最主要的子类,分别是pypdevs.DEVS.AtomicDEVS、pypdevs.DEVS.CoupledDEVS以及pypdevs.simulator.Simulator,分别对应于原子模型、耦合模型以及仿真器。
使用方法: 主要通过继承关系来重写父类方法,实现DEVS框架的搭建,与使用pytorch搭建神经网络模型时对forward函数的重写方式类似。
原子模型AtomicDEVS的常用方法
extTransition: 外部事件转移函数,返回self.state
intTransition:内部事件转移函数,返回self.state
confTransition:确认转移函数,返回self.state
outputFnc:输出函数,返回一个字典
timeAdvance:时间步长ta,返回一个数值或者无穷
Finalize: 确认最后的状态
耦合模型CoupledDEVS的常用方法
select: 选择函数
getModelLoad:获得耦合模型的原子模型数量
finalize:仿真前确认最后的状态
flattenConnections:对每一个原子模型端口操作
unflattenConnections:对每一个原子模型进行端口操作
addSubModel:增加子模型
removeSubModel:移除子模型
disconnectPorts:将两个端口解耦
connectPorts:将两个端口连接起来
仿真器Simulator的使用方法
在搭建完原子模型和耦合模型之后,将其添加入DEVS仿真器即可运行:
model = Couplemodel() # 实例化耦合模型
sim = Simulator(model) # 加入到仿真器中
sim.setVerbose(None) # 打印离散事件
sim.setClassicDEVS() # 使用经典(串行)的DEVS
sim.setTerminationTime(20) # 设置仿真运行的总时间
sim.simulate() # 开始仿真
4 pypdevs仿真实例
使用一个grid-world为背景的简单智能体模型来进行示例:
模型简介: 1、智能体在二维环境中可以感知到自己上下左右一个间隔的距离,在grid-world中设置两种障碍物。2、一种障碍物在被看到后智能体会靠进去待一个时间步长、另一种障碍物智能体在看到他时会远离它,墙壁属于第二种障碍物。3、智能体完全遵循随机行走,完全随机选择四个方向,但是会尽可能地不走重复的路。
模型结构:
Walker向Env环境发送自己的位置(state),Env向Walker发送其下一步可以走的位置,以及其上下左右四个可感知范围内是否存在障碍物的相关信息。
程序:
from pypdevs.DEVS import *
from pypdevs.infinity import INFINITY
from pypdevs.simulator import Simulator
import random
# 步骤:
# 1 原子模型
# 2 耦合模型
# 3 加入仿真器
# 1-搭建原子模型
class Walker(AtomicDEVS):
def __init__(self):
AtomicDEVS.__init__(self, "Walker")
self.state = (0,0) # 起始状态
self.time_step = 1.0 # 时间步长都取1
self.outport = self.addOutPort("outport") # 增加端口
self.inport = self.addInPort("inport") # 增加端口
self.isbegin = False
self.visited = []
def timeAdvance(self): # ta 要的是返回值
return self.time_step
# 输出
def outputFnc(self): # ta 要的是返回值
# Our message is simply the integer 5, though this could be anything
print(self.state)
return {self.outport: self.state}
def intTransition(self): # ta 要的是返回值
self.visited.append(self.state)
with open("state.txt", "a", encoding="utf-8") as f:
print(self.state, file=f)
return self.state
# 外部事件转移函数
def extTransition(self, inputs):
self.isbegin = True
self.possible_states_dict = inputs[self.inport] # 找到inputs里的对应的字典端口,必是字典格式
# print(self.possible_states_dict)
# print("agt:", 666)
self.ways = self.possible_states_dict["ways"]
self.target1 = self.possible_states_dict["target1"]
self.target2 = self.possible_states_dict["target2"]
# 得到了[(),(),(),()], [()],[()]
temp = []
for eve in self.ways:
if eve not in self.target2:
temp.append(eve)
is_no_way = True
for eve in temp:
if eve not in self.visited:
is_no_way = False
if not is_no_way:
for eve in self.visited:
if eve in self.target1:
self.target1.remove(eve)
if eve in self.ways:
self.ways.remove(eve)
if len(self.target1) > 0:
self.state = self.target1[random.randint(0, len(self.target1) - 1)]
return self.state
if len(self.target2) > 0:
temp = []
for eve in self.ways:
if eve not in self.target2:
temp.append(eve)
self.ways = [eve for eve in temp]
self.state = self.ways[random.randint(0, len(self.ways)-1)]
return self.state
self.state = self.ways[random.randint(0, len(self.ways) - 1)]
return self.state
# 搭建原子模型
class Gridworld(AtomicDEVS):
def __init__(self):
AtomicDEVS.__init__(self, "Gridworld")
self.state = {}
self.env = [
[0, 0, 0, 0, 0],
[0, 1, 0, 0, 0],
[0, 0, 0, -1, 0],
[0, 0, 1, 0, 0],
[0, 0, 0, 0, 0]
] # 5*5
self.env_size = 5
tmp = [[(i,j) for i in range(self.env_size)] for j in range(self.env_size)]
self.all_state = []
for eve in tmp:
for ev in eve:
self.all_state.append(ev)
self.time_step = 1.0 # 时间步长都取1
self.inport = self.addInPort("input")
self.outport = self.addOutPort("output")
self.isbegin = False
def timeAdvance(self):
if self.isbegin:
return self.time_step
else:
return INFINITY
def outputFnc(self):
return {self.outport: self.state}
def extTransition(self, inputs):
agt_state = inputs[self.inport] # 找到inputs里的对应的字典端口,必是字典格式
self.isbegin = True
tmp = [
(agt_state[0]+1, agt_state[1]),
(agt_state[0]-1, agt_state[1]),
(agt_state[0], agt_state[1]+1),
(agt_state[0], agt_state[1]-1)
]
ways = []
target1 = []
target2 = []
for eve in tmp:
if eve in self.all_state:
ways.append(eve)
if self.env[eve[0]][eve[1]] == 1:
target1.append(eve)
if self.env[eve[0]][eve[1]] == -1:
target2.append(eve)
self.state["ways"] = ways
self.state["target1"] = target1
self.state["target2"] = target2
return self.state
def intTransition(self):
self.isbegin = False
return self.state
# 搭建耦合模型
class CQueue(CoupledDEVS):
def __init__(self):
CoupledDEVS.__init__(self, "CQueue")
self.walker = self.addSubModel(Walker())
self.gridworld = self.addSubModel(Gridworld())
self.connectPorts(self.walker.outport, self.gridworld.inport)
self.connectPorts(self.gridworld.outport, self.walker.inport)
# 进行仿真
model = CQueue()
sim = Simulator(model)
sim.setVerbose(None)
sim.setClassicDEVS()
sim.setTerminationTime(20)
sim.simulate()