RePast教程.docx
《RePast教程.docx》由会员分享,可在线阅读,更多相关《RePast教程.docx(44页珍藏版)》请在冰豆网上搜索。
RePast教程
Repast
Javacswarm脚本编译模型,用Javaswarm脚本运行模型。
作为一个开源项目,在遵循GNU协议的基础上,所有人都可以分享Swarm的资料,这也意味着所有人都可以自由地奉献自己的智慧。
因此,对Swarm感兴趣的研究人员可以结合自己的研究需要和建模经验,提出相关的设想,参与到Swarm的研究中去,本节后面的资源链接中列出了相关网址。
Repast(RecurslvePorousAgentSimulationToolkit)是芝加哥大学社会科学计算研究中心研制的多主体建模工具,她提供了一系列用以生成、运行、显示和收集数据的类库,并能对运行中的模型进行“快照”,记录某一时刻模型的当前状态,还可以生成模型运行过程中状态动态演化的视频资料。
Repast从Swarm中借鉴了不少的设计结
构和方法,所以常常称Repast为类Swarm的架构。
一、设计思想及目标
由于Swarm对建模者来说还是有些过于复杂,Repast项目希望提供一系列简化Swarm模型开发的Java类库。
然而,随着JavaSwarm①版本的推出,这种仅仅希望作为Swarm的Java扩展的想法很快就被摒弃了。
设计者们开始尝试使用Java语言设计一个完全独立的模型平台,而不再是从Swarm中做一些现成的提取有外围的包装。
RePast项目拟订了三个设计目标:
使用方便、容易学习和容易扩展。
设计者通过让模拟软件的底层结构具备抽象性、可扩展性以及“良好”的表现来实现这些目标。
1.抽象性
RePast的设计借鉴了很多别的主体建模软件,汲取了各个软件中最优秀的设计思想。
类库设计时充分应用了面向对象和设计模式的思想,因此RePast的整个类库的结构非常明晰优美。
类库提供了普通常用的底层抽象库(如安排时序、显示、数据收集等类库),类库还提供了一些用以建立表层元素的常用类。
这些类可直接使用,也可以根据需要继承和扩展。
与Swarm一样,Repast还设计了一些关键的抽象数据结构,其中一些结构直接模仿了Swarm,如时序表等。
2.可扩展性
关键数据结构的抽象设计让Repast具备了可扩展的能力。
为了充分从Swarm的抽象结构中获得方便,Repast继承了Swarm时间测试的设计方法,这对于提高其扩展性十分有利。
此外,Repast还引人了设计模式中的一些经典抽象结构,使得其扩展性得到进一步的提高。
如用设计模式中的综合模式实现时序的安排机制(时序表对象和各种行为类),由于这种综合模式允许终端用户在建模编程时,能够对单独行为和复合行为进行统一编码处理,因此模型中的时序安排机制便变得很清晰直观,且易于扩展。
3.“良好”的表现能力
表现能力是指应用该平台建立应用研究模型时,该平台能否有效地适用于实际应用领域;如能否有效地用平台的开发接口把问题表述成计算机模型,模型能否在可承受的效率开销下运行,等等。
“良好”的表现能力是指:
在不影响别的优越特性前提下,可接受的表现能力。
当表现能力的最优化不是设计的主要目标时,设计者的注意力应集中在如何减少对象的生成开销,以达到一种能够接受的运行速度。
RePast可以算得上比较好地完成了这一目标,甚至有所超越,相比其他模拟软件平台,Repast提供了更好的表现能力。
此外,随着Java虚拟机性能的提高,Repast模型的速度也会得到改善。
虽然建模时仍然必须学习一门程序设计语言:
Java或Python,但Repast仍然是一款相当方便易用的软件。
用Java作为实现语言避免了内存泄漏的问题,且Java的跨平台特性使得在不同的平台上的安装和使用也很简便。
从Repast3.0版开始,模型的设计语言有了更多的选择;Repast3.0提供了Java版、Python版和DotNet版三个版本让最终用户选择安装,除Java和Python外,还可以应用Dot-Net框架下的各种程序设计语言编制模型。
_、Repast的体系结构
RePast建模相当于设计一个状态机,状态机的核心状态是模型中所有成员的集体状态属性集合。
成员则分为底层结构和表层结构。
底层结构是各种各样用于运行模型、显示和收集数据等架构的机制;表层结构是设计者创立的模型。
底层结构的状态就是模型的种种显示状态、数据收集对象的状态等等;表层结构的状态指模型的描述状态,比如所有主体变量的当前值,模拟环境空间的当前状态值,或他们运行的空间以及别的可能有的各种表层对象(例如聚合“协同”的对象统计值等)。
在这种状态机模式下,所有对状态机的改变都通过同一对象界面接口来实现,这个对象界面接口是SirnModellmnl类。
这种设计为建模者减轻了学习负担,也简化了在工具包的功能不足时的扩展编程。
通过继承SimModellmpl来建模,在用户模型与SimModellmpl中间一般还会有一个Template结构。
各部分间的路径和层次关系参见图4—12与图4—13。
图4—12的目录结构图显示了Repast自带文件、Template及用户利用Repast建立的模型在计算机中存放的路径关系。
图4—13层次关系图显示了一个典型的继承自SimMod(,IImpl的模型和Template结构间的层次关系。
三、类库简介
Repast共有近130个类,封装在6个库中,下面是6个类库的简要介绍。
1.分析库Analysis
分析库中的类是用来聚集、记录数据以及建立数据表。
通过使用DataRecorder类,建模者能将收集到的数据分类整理,并将这些数据以表格格式写人文件。
2.引擎库Engine
引擎类负责建立、操纵和运行模型。
SimModel接口(Interface)是Repast中所有模型的超类(继承树状图表示中的根类)。
作为SimModel一个子类,SimModellmp类可以作为绝大多数用Repast创立的模型的基类。
控制类(BaseController,Controller,Batch-Controller)负责通过图形用户界面处理用户交互,或通过批处理参数文件自动处理交互。
时序表及相关行为类主要用来改变模型中的状态。
3.博弈库Games
博弈库中包含了一些建立对策论模型(比如囚徒困境等)所用的基本类。
4.因形用户界面库GUI
GUI类负责实现模型的图形可视化,包括对图形界面显示的快照功能以及制作模型运行的活动录像(影像资料,quicktime格式)。
各种。
Dsplay类与Space空间库中的类协同工作,把空间位置关系的抽象和空间关系的显示分离开来,可以针对不同的空间特征设计不同的可视化显示方案。
通过一个显示界面(Displaysurface),。
Painter类处理这些空间库在屏幕上的显示,而显示界面本身就能够完成对所显示对象的探测。
对可视化的模拟对象点击鼠标左键,就会弹出一单独舷窗口,其中显示点中主体的当前状态。
5.空间库Space
空间类是表述各种空间的基础容量类(在通常的计算机术语中,容量类是指以包含其他类作为自己的元素的类,比如数组、集合、链表等)。
空间类包括二维表空间(有边界)、圆环面空间(上下无边界)等。
这些空间类允许在x,y坐标轴的基础上对对象进行括人和检索。
空间库中还包含了一些节点类和链接类,以方便建立基于网络的模型。
空间类和在GUI库中的显示类协同操作,以实现空间和对象的可视化。
在RePast中,空间(sPaces)的存在有两种目的:
(1)作为空间型主体的基类,比如附带的示例模型SugarscapeandLif;,中的SugarSPace或InfiniteLifeSPace;
(2)作为主体的容器,定义iE体间的空间位置关系。
RePast提供以下空间类:
Diffuse2D二维散射的离散近似。
空间本身是一个上下左右都闭合的圆环形的(toriodal)网格,格子都有两层。
Obect2DGrid离散的二维网格,单元格中有对象。
Object2DTorus离散的二维圆环面,单元格中有对象。
Multl2DGrid二维网格,单元格中可以包括一个以上的对象。
每个单元格中的对象的顺序不确定。
OrdMMulti2DGrid二维网格,单元格中可以包括一个以上的对象。
每个单元格中的对象的顺序都是先进先出。
Multi2DTOrus二维圆环面,单元格中可以包括一个以上.的对象。
每个单元格中的对象的顺序不确定。
OrderedMulti2DTorus二维圆环面,单元格中可以包括一个以上的对象。
每个单元格中对象的顺序都是先进先出。
Diffuse2DHexagoual二维散射的离散近似。
空间本身是一个to-riodal(甜甜圈形的)六边形网格,格子都有两层。
Object2DHexaeonalGrid离散的二维六边形网格,单元格中有对象。
ONeconHexgonalTorus离散的二维六边形圆环面,单元格中有对象。
Multi2DHexalGrid二维六边形网格。
单元格中可以包括一个以上的对象,每个单元格中对象的顺序不确定。
OrderedMulti2DRexagonalGrid二维六边形网格。
单元格中可以包括一个以上的对象,每个单元格中对象的顺序都是先进先出。
MultiZDHexasonaITorus二维六边形圆环面。
单元格中可以包括一个以上的对象,每个单元格中对象的顺序不确定。
OrderedMultiZDHexagonalTorus二维六边形圆环面。
单元格中可以包括一个以上的对象,每个单元格中对象的顺序都是先进先出。
RasterSPace表现地理数据的二维网格。
每个单元格可以包含一个对象,在这个空间中移动可以用距离、坐标系或者离散的单元格位置。
更多空间类信息可参见Repast的API文档。
另外值得注意的是,网络空间与空间是完全不同的,将在4.3.6节网络模型中介绍网络空间。
此外,前面提到的六边形指的是单元格的形状,所以在六边形的网格或者回环面中,每个单元格都是六边形的,因此有六个邻居。
这些空间类都有通过坐标系对item进行插人和删除的方法,还有能返回相邻单元格的item列表的方法,以及找出相邻单元格中最大或最小item的方法等。
至于什么是最大、最小,可以由用户自己定义。
用空间类存放主体的坐标方便了增删主体和排序主体的操作,也方便针对一个主体的邻居空间进行查询。
通常在一个特定空间中,所有的主体都由该空间的实例变量负责主体位置的部署。
如在build-Modlel方法中,可能有下述代码:
Object2DGridspace=newObject2DGrid(spaceWidth,spaceHeight);
for(inti=0;iinx,y;
do{
x=Random.uniform.nextIntFromTo(0,space.getSizeX()-1);
y=Random.uniform.nextIntFromTo(0,space.getSizeY()-1);
}while(space.getObjectAt(x,y)!
=null);
MyAgentagent=newMyAgent(x,y,space);
space.putObjectAt(x,y,agent);
agentList.add(agentt);
}
上边第一行代码创建了一个ObjectZDGrid空间,Do循环返回随机的x,y坐标,如果这个坐标位置被占用则继续返回新的坐标位置。
然后创建了主体,把主体的弓佣(reference)按这些坐标加人到空间。
如果空间是可重空间(Multispaces),则不需要核查空间中的
单元格是否被占用,可重空间中一个单元格可放置多个主体。
无论选择哪种空间类型,主体在空间中都有一个引用,并能获得自己的X,y坐标,能把自己从先前占用的单元格中移出,加人到一个新的单元格中去。
如:
SpaceputObjectAt(x,y,null);
x=newX;
y=newY;
SpaceputObjectAt(x,y,this);
上述代码通过把单元格的占有者设为空(null)来移除一个主体。
这段代码是针对这个主体发生的,所以用this来引用这个*主体,把它放到新的单元格中去。
newX和newy在代码的外部就被设定好了,所以这里不再核查新的单元格是否已被占用。
6.Util类库
Util类库中只有一个类:
SimUtiltities。
该类提供了一些产生无序表单、显示信息对话框等静态方法。
除了Repast自己提供的类,Repast还充分利用了第三方提供的扩展类库,尤其是Colt库。
Colt库通过MersenneTwisterDZ具为Repast提供随机数据,而MersenneTwister被认为是目前所知道最好的假随机数生成器之一。
Colt库中还包含其他各种能在ReFast中使用的随机数生成器和随机分布。
四、建模基本过程
Repast模型有两种运行方式:
批处理方式运行和非批处理方式(也可称图形交互方式)运行。
批处理运行需要一个特殊格式的参数文件,在这个文件中要详细给出模型各个参数的起始值、终止值和增量值,以及运行的次数等;有了参数文件后,模型就可无须用户干预连续重复运行。
一个非批处理的运行则需要通过图形用户界面来交互地启动和终止模型,用户可以通过图形界面来设定初始参数值,可以在运行过程中图形化地监控主体和模型的各种状态。
下面介绍编写模型的一般步骤,需要特别注意以下问题:
如何能用参数文件来设定初始参数?
如何让用户能够图形化地设定起始参数?
如何允许在运行时对模型进行图形化监控?
为了让模型支持这些交互需要在设计中要做哪些准备?
等等。
数据结构上,RePast模型一般至少有两个由用户编写的类:
主体类和模型类。
主体类描述主体的行为(例如,在博弈中合作或者对立),模型类负责控制模型的创建和运行。
主体类大多需要专门构造,尽管RePast已经通过GameAgent接口以及Game抽象类为合作类型的主体提供了最基本的支持。
如果希望主体能够被图形化显示,主体类必须实现某一类型的Drawable接口,后面在介绍到Repast内部机制GUI时还会具体介绍Drawable接口类的使用。
如果用户希望主体是可探测的,即主体状态的可视化和可运行时干预设置,则主体的属性必须用附属方法模式(Accessor)编写,即以get和set打头的固定格式的方法,用于属性的存取。
从过程的观点看,利用RePast建立一个基于主体的模型通常包括两步。
第一步是设置模型,为模型的运行做准备;第二步是实际运行模型的动作规定。
在RePast中,模型的运行按时间步(timesteps)或标记(tick)来推进。
(注:
在本节中交替使用时间步和标记两种术语指谓这一概念)。
每一个标记上,各个主体发生一些行为或状态的变化,主体们在这个标记上的行为建立在之前行为的结果上。
以“囚徒困境”(iteratedprisoner’sdilemma,简称PD)模型为例,创建步骤中包括创建两个参与者,并且为他们每一个提供初始策略(例如:
TIT_FORTAT)。
在每一个标记上,每个参与者进行一次合作与否的决策,他们当前的决策取决于各自的策略,也可以综合考虑前一次决策的结果。
对于模型设计者来说,设计一个Repast模型所必须做的全部工作就是,在前面提到的两个步骤中设置模型阶段需要准备哪些工作?
在一个标记上各种主体会发生什么行为或状态变化?
Repast自带的SimPfeMoel类提供分步实现模型的框架,接下来我们将首先介绍如何通过扩展该类建立自己的模型,接着再介绍如何在一个实用的模型中设置参数(Parameter)使其可设置、可在运行时探测。
1.继承SimPleModel、创建立模型类
尽管不一定必须应用SimModel接口,但应用这个接口可将底层和表层的建立过程分到不同的方法组中,使得模型的结构与设计过程清晰明了。
用SimModel和Template类建模的典型的模型结构包括以下三个方法:
(l)privatevoidbuildModel()
buildModel负责创建代表模型的底层结构部分。
主体对象、环境对象,还有一些可选的数据收集对象常在此创建。
如,在糖域模型中,SugarModel在buildModel()里构建了SugarAgents和Sug-arSpace
(2)privatevoidbuildDisplay()
bllildDi8PIPy建立了那些用于处理向用户显示模拟的表层结构部分,所以那些只以批处理方式运行的模型很可能不需要实现这个方法。
SugarModel通过它建立了显示主体和图的类。
实际创建Dis-playsurface对象则通常放在一个预定义的setup()方法中,稍后再说明这个方法。
(3)privatevoidbuildschedule()
buildschedule建立负责改变模型状态的时间表——何时运用什么方法调用什么对象的时间表。
SimpleModel作为模型类的基类,可根据需要继承扩展(特定化)。
具体实现如下:
Importuchicago.sre.sim.engine.SimpleModel;
PublicclassMyModleextendsSimpleModel{…
}
第一行导人了SimpleMOdel,第二行继承了SimpleModel。
下面按建模过程的两步法建立完整的模型类:
第一步:
设t模型
SimPleModel提供了两个方法作为实现这一步方便填充的框架:
setuP()和buildModel()。
他们可以按如下方式使用(以IPD为例):
Importuchicago.src.sin.engine.SimpleModel;;
PublicclassMyModleextendsSimpleModel{
PublicstaticfinalintTIT_FOR_TA=0;
PublicstaticfinalintALWAYS_DEFECT=1;
Privatep1Strategy=TIT_FOR_TAT;
Privatep2Strategy=ALWAYS_DEFECT;
…
Publicvoidesetup(){
Super.setup();
P1Strategy=TIT_FOR_TAT;
Privatep2Strategy=ALWAYS_DEFECT;
}
PublicvoidebuildModel(){
Player1p1=newPlayer(p1Strategy,p2);
Player2p1=newPlayer(p2Strategy,p1);
agentList.dss(p1);
agentList.dss(p2);
}
}
在setuP()中,首先调用super.setup()使SimpleModel实现自身的创建,然后设定了行为者的策略。
这里假设行为者的策略可以通过用户的干预,或者可能在先前的模型运行过程中从缺省值改变。
setup()让模型的变量值重新回到缺省值。
setup()在模型启动时或单击控制条中“setup”键时被调用。
当setup()启动一个模型时,调用buildModel()来创建模型所需的对象,所以应在这里创建主体对象,并将其加人对象的列表agentList中。
agentList是由SimPleModel为此提供的一个ArrayList。
在Setup时所有的参数都恢复到缺省值,所以在这里不能构造基于参数值的对象。
在上面的例子中,两个参与者中一个根据其初始策略决策,另一个根据对手的策略决策。
模型的执行顺序是:
setup()调用在先,buildModel()调用在后。
如上所述setup()在模型一被启动就被调用,当点击“set-up”键时也会被调用。
buildMOdel()在模型运行时(即“run”或“step”键按下时)被调用,这就为用户通过图形界面接口改变变量提供了机会。
第二步:
实际运行模型的动作规定
完成模型的设置后,第二步考虑的就是要如何限定模型在每一个时间步上的行为。
SimpleModel为完成这一步提供了三种方法:
prestep()、step()和poststep()。
在每一个“标记”(tick)上他们依照次序被执行:
首先是PresteP(),然后是step(),最后是postStep()。
要特别注意的是,区分steP()中的核心行为与必要的之前和之后的过程。
由于下面的例子并不需要之前和之后的过程,所以只实现step()一个方法。
Importuchicago.src.sim.engine.SimpleModel;
PulicclassMyModelextendsSimpleModel{
PublicstaticfinalintTIT_FOR_TAT=0;
PublicstaticfinalintALWAYSZ_DEFECT=1;
Privatep1Strategy=TIT_FOR_TAT;
Privatep1Strategy=ALWAYSZ_DEFECT;
…
Publicvoidesetup(){
Super.setup();
P1Strategy=TIT_FOR_TAT;
Privatep2Strategy=ALWAYS_DEFECT;
}
PublicvoidebuildModel(){
Player1p1=newPlayer(p1Strategy,p2);
Player2p1=newPlayer(p2Strategy,p1);
P1.set()therPlayer(p2)
P2.set()therPlayer(p1)
agentList.dss(p1);
agentList.dss(p2);
}
Publicvoidstep(){
Intsize=agentList.size();
for(inti=0;IPlayerp=(Player)agentlist.get(i);
p.play();
}
}
}
此处的steP()方法中,首先将每个参与者从对象列表agentList中调出,然后逐个调用play()。
此时的假设是,当调用play()时,一个参与者与另一个参与者博弈。
在step()方法中,常常需要遍历所有的主体,并且调用任何有关它们之间交互的方法。
当模型运行的时候,step()方法将会在每个时间步(标记)中被执行。
一个不同的决策可能需要一个PresetP()或者PoststeP()方法。
例如,在一个有许多参与者的合作决策中,在每个参与者与它的邻居进行博弈并产生结果后,每个参与者选出邻居中最好的策略,并将自己的策略相应地进行调整。
在这种情况下,实际的决策将在steP()方法中决定,对邻居的选择和策略调整将在PoststeP()方法中发生。
Repast还可实现更为复杂的主体行为、模型事件等时间序列安排,甚至可实现动态的时间序列,而使用SimpleModel的目的是为了简化时间序列的设计。
时间序列机制的相关内容,包括具体的使用方式,将在后文“典型内部机制”部分做更详细的介绍。
除上述骨架以外,为了完成SimModel接口,用户模型还必须定义以下方法(如果没有定义这些方法,模型将拒绝编译)。
(i)PublicString[」getlnitParam()getlnitParam()将返回一个用户希望显示和操作的模型初始化参数名序列,一个字符串数组。
(ii)publicvoidbegin()begin(