python-ABM-mesa库:AgentBasedModel简单教程:可视化与排队论
python-mesa:Agent Based Model 简单教程
java调用python模型
Mesa是⽤于构建,分析和可视化基于代理的模型(Agent Based Model)的模块化框架。
基于代理的模型是⼀种计算机模拟,涉及多个实体(代理),这些实体根据它们的编程⾏为互相作⽤和交互。代理可以⽤来代表活细胞,动物,个⼈,甚⾄整个组织或抽象实体。有时,我们可能会对系统中各个组件的⾏为有所认识,并希望研究各个部分在系统整体层⾯上产⽣的⾏为和效果;⽽在其他时候,我们可能希望研究系统的整体⾏为。
它适合研究各种模型仿真主体(代理⼈,也就是Agent)的合作与竞争关系。
安装:
pip install mesa
⽰例1:随机发红包游戏
假设有⼀个,⾥⾯有⼗个⼈,游戏规则是这样的:
每个⼈初始有⼀元钱,游戏开始后,每轮每⼈随机选定⾥的⼀个⼈(可以是⾃⼰)发⼀个1元红包。如果钱数为0,本轮就不⽤发。
游戏进⾏10轮,试着⽤代码描述这样的过程。
过程有三步:设置模型,添加调度器,进⾏仿真。
仿真主要有Agent和Model两类,⼀般在Agent中⽤⾯向对象的⽅式定义不同Agent中的竞争合作关系;⽽Model类⽤来管理若⼲个Agent,
from mesa import Agent, Model
from mesa.time import RandomActivation
class MoneyAgent(Agent):
"带有固定初始财富的代理⼈Agent。"
def__init__(self,id, model):
super().__init__(id, model)# ⾃⾝的id和归属的模型。
self.wealth =1
def step(self):
if(self.wealth ==0):
return
otherAgent = self.random.del.schedule.agents)
# 在模型的多个Agent中,随机选出⼀个Agent
otherAgent.wealth +=1# 给他⼀块钱的红包。相当于是蒙特卡洛仿真⼀下。当然,过程结束之后谁得到多少钱是随机的。
self.wealth -=1
class MoneyModel(Model):
# 管理代理⼈的模型。
def__init__(self, N):
self.agentNum = N
self.schedule = RandomActivation(self)
# 创建Agents
for i in range(self.agentNum):
a = MoneyAgent(i, self)
self.schedule.add(a)
def step(self):
self.schedule.step()
最后调⽤matplotlib进⾏绘图。绘出的图像是每位代理⼈的财富(纵轴)与⼈数(横轴)关系的直⽅图。
import matplotlib.pyplot as plt
agent_wealth =[a.wealth for a in model.schedule.agents]
plt.hist(agent_wealth)
如果将这样的游戏反复进⾏⼀百次,每次游戏进⾏⼗轮。将每次游戏后每个⼈的钱数看做观察的样本,那么应该会产⽣100次×10⼈
=1000个样本。这⼀千个样本中,⾦钱是怎样分布的?
⽰例2:基于MESA的Web可视化模块的排队论模型(原创)
mesa库与netlogo类似,在内部的基础上,还有⼀个创建仿真界⾯的功能。这个仿真界⾯前端使⽤web技术,后端是基于python的tornado开发的,可以说是适应了Web技术⼤⾏其道的潮流吧。
当然这种技术也有缺点。缺点之⼀就是python和javascript的速度都不怎么样,⽐起基于java的netlogo还是⼒有不逮;其次,⽹络通信的速度也⼀般,最⾼仿真速率只有20fps,也就是说⼀秒钟刷新20次。
不多说了,先上代码吧。
from mesa import Agent
from mesa import Model
from mesa.datacollection import DataCollector
from mesa.space import Grid
from mesa.time import RandomActivation
import numpy as np
from dules import CanvasGrid, ChartModule, PieChartModule, TextElement
from mesa.visualization.ModularVisualization import ModularServer
from mesa.visualization.UserParam import UserSettableParameter
import mesa
print(mesa.__file__)
# 可以修改服务器函数中的⼀些功能.
class StatusElement(TextElement):
'''
显⽰当前服务的状态。.
'''
def__init__(self):
pass
def render(self, model):
return"⽬前已经服务的⼈数: "+unt_type(model,'served'))
class QueueModel(Model):
"""
⼀个简单的排队论模型。
"""
def__init__(self, height=100, width=100, meanServiceTime=10, mtba=10):# mtba的意思是平均到达的时间间隔# Initialize model parameters
self.height = height
self.width = width
self.customerNum =0
self.interval =0
# Set up model objects
self.schedule = RandomActivation(self)
self.datacollector = DataCollector(
{"serving":lambda m: unt_type(m,"serving"),
"served":lambda m: unt_type(m,"served"),
"waiting":lambda m: unt_type(m,"waiting")})
self.running =True
llect(self)
def generateCustomer(self):
c = Customer(self.customerNum, self, anServiceTime)
self.schedule.add(c)
self.customerNum +=1
def step(self):
#for i in range(10):
self.schedule.step()
llect(self)
if(self.interval <=0):# 如果产⽣顾客的间隔归零
self.interval = np.random.poisson(ba)
else:
self.interval -=1
@staticmethod
def count_type(model, customer_condition):
"""
Helper method to count trees in a given condition in a given model.
"""
count =0
for customer in model.schedule.agents:
dition == customer_condition:
count +=1
return count
class Customer(Agent):
def__init__(self, pos, model, meanServiceTime):# mtba是顾客到达的平均时间间隔(暂时是服从简单的random分布)
super().__init__(pos, model)
self.pos = pos
self.serviceTime = np.random.poisson(lam=meanServiceTime)# 这⼀步是服务时间,可以做成随机⽣成的!
def step(self):
"""
If the tree is on fire, spread it to fine trees nearby.
"""
dition =="waiting":
if(self.checkNextAgent()==False):# 如果下个单元格没有顾客占⽤的话
if self.pos[0]==18:
dition =="serving":
self.serviceTime -=1
if(self.serviceTime <=0):
def checkNextAgent(self):# 检测下⼀个单元格是否有顾客占⽤。
absoPos = vertAbsoPos(1,0)
nextAgent = id.get_cell_list_contents([absoPos])
if nextAgent ==[]:
return False
else:
return True
def convertAbsoPos(self, dx:int, dy:int):# 输⼊相对这个Agent的位置返回绝对位置
x, y = self.pos[0]+ dx, self.pos[1]+ dy
width = id.width
if(x >= width):
x -= width
elif x <0:
x += width
if(y >= width):
y -= width
elif y <0:
y += width
return(x, y)
def move(self, dx:int, dy:int):
new_position =(self.pos[0]+ dx, self.pos[1]+ dy)# 向右为正,向上为正。
def get_pos(self):
return self.pos
COLORS ={"serving":"#00AA00",
"waiting":"#880000",
"served":"#000000"}
def serviceTablePotrayal(customer):
if customer is None:
return
portrayal ={"Shape":"rect","w":1,"h":1,"Filled":"true","Layer":0}
(x, y)= _pos()
portrayal["x"]= x
portrayal["y"]= y
portrayal["Color"]= dition]
return portrayal
return portrayal
height =1
width =20
canvas_element = CanvasGrid(serviceTablePotrayal, width, height, width *20, height *20)
customer_chart = ChartModule([{"Label": label,"Color": color}for(label, color)in COLORS.items()])
statusElement = StatusElement()
pie_chart = PieChartModule([{"Label": label,"Color": color}for(label, color)in COLORS.items()])
model_params ={
"height": height,
"width": width,
"mtba": UserSettableParameter("slider","顾客到达的平均时间(MTBA)",10,1,20,1),
"meanServiceTime": UserSettableParameter("slider","处理每位顾客要求所需的时间",10,1,20,1)
}
server = ModularServer(QueueModel,[canvas_element, customer_chart, statusElement],"Forest Fire", model_params)
server.launch()
# --------------------------------以下被注释掉的内容是不启动图形化界⾯也可以运⾏的。特点是仿真速度⾮常快。
#
# model= QueueModel(meanServiceTime=5,mtba=4.5)
# time = 0
# while(1):
#    time+=1
#    model.step()
#    print(time,"served: " + unt_type(model,'served')),"waiting: " + unt_type(m
odel,'waiting')))
⾸先看原理。
QueueModel这个模型就是仿真模型了,⽽Agent就是Customer这个类。这些东西和⽰例⼀相同。
数据收集器
然后就是datacollector——这个数据收集器是做啥的呢?当然是收集当前的局部变量⽤的!定义顾客有三种状态,serving:正在被服
务,served已经接受过服务,waiting:正在排队。就这三种状态。⽽datacollector的⽬的就是统计每种状态对应的⼈数:排队的,已经服务完了的,还有正在服务的。
然后写⼀下count_type⽅法,这个⽅法⽤于统计处于各种条件下的顾客⼈数。
排队本⾝的问题
在模型⽅⾯,写⼀下Model中的generateCustomer这个⽅法,⽤于⽣成顾客。⽣成顾客时,同时⽣成⼀个服从泊松分布的随机数
self.interval。每次step的时候,随机数减⼀;减到零的时候,⽣成下⼀个顾客。
每个step中,若前⽅⽆⼈,则顾客向服务台运动⼀个单位。
着⾊与控件
定义⽹格画布控件:
def serviceTablePotrayal(tree):
if tree is None:
return
portrayal ={"Shape":"rect","w":1,"h":1,"Filled":"true","Layer":0}
(x, y)= _pos()
portrayal["x"]= x
portrayal["y"]= y
portrayal["Color"]= dition]
return portrayal
height =1
width =20
canvas_element = CanvasGrid(serviceTablePotrayal, width, height, width *20, height *20)