How to Build a Model – 2(如何构建一个模型-2)

时间:2024-03-10 13:29:43

  本文档描述了如何利用SimpleModel类创建一个Repast模型。从代码上来看这样的模型更复杂,但是可以汲取模型所有的优点。本文档的目的是为了那些建立拥有Reppast功能的模型的用户或者有写基于agent仿真经验的人。如果没有实践经验的其他人请参考如何创建一个Repast模型-1

注意:这篇文档最好结合仿真例子一起读。

OverView

  利用Repast仿真类型可以分为两大类:批处理运行和非批处理运行。批处里仿真通过读取一个特殊格式化参数的文件来完成,这个文件详细的说明乐模型开始参数与结束参数,以及怎样增加这些参数和运行model的个数。然后仿真就开始执行而无须人工干预。

非批处里的运行需要用户通过图形用户接口来启动和停止,允许用户图形化方式设置启动参数。另外非批处理仿真允许用户在运行过程中以图形化的显示和操作(例如监测)一个agent和模型的状态。

接下来就介绍下实现这两种仿真的基础,特别注意对于一个模型的参数文件中的初始化参数设置、用户图形化的设置初始化参数和允许监测发生所必须做的。

The Agent

  批处里和非批处里仿真大体上相相似。不管是哪种处理方式,一个通过Repast实现的典型仿真至少包含两个用户实现的类,agenet类和model类(请看样本仿真详细实例的源码)。Agent类在很大程度上通过GameAgent接口和Game抽象类来模拟特定的实体,Repast为交互式代理提供最基本的支持。如果agent想要显示,它需要实现Drawable接口(更多信息请看 How To Create Displays)。如果用户想要监测一个agent,也就是说想要图象化的方式显示控制agents的状态,agent必须用访问器方法模式去编码实现。

The Model

一个模型,比如SugarModel.java或者EnnModel.java(in repast/demos/sugarscape/src/src/uchicago/src/sim/sugarScape and repastdemos/enn/src/src/uchicago/src/sim/enn respectively) 都建立和控制着Repast仿真的显示和基础结构。所有Repast仿真模型必须实现SimModel接口。Repast提供了一个抽象类SimModelImpl,它实现了部分接口,方便大部分类去扩展它。

假设用户通过写自己的model来扩展SimModelImpl,典型的model类需要包含以下部分

  • Infrastructure and Representation 变量

  典型model类包含的变量即既是infrastructure 和 representational 类型。Infrastrusture 变量就像一个时间表,是agent等的集合类。例如,SugarModel.java有一个Schedule变量和显示agent和agent环境的DisplaySurface变量。representation变量的最大一部分就是模型运行的初始化参数。例如,SugarModel包含一个numAgents变量,它是用来配置仿真模型中sugar agent的数量。

任何继承了SimModelImpl的模型也继承了rngSeed变量,这个变量能通过getRngSeed 和 setRngSeed来访问。

  • Repast Template Methods

  这些模板方法尽管不是SimModel接口必须的,将基础结构和关键性步骤设置过程划分成连贯的组。所以,不必须但是鼓励用户用这些方法来构建他们的模型。

  1)private void buildModel()

  buildModel()方法的职责是创建仿真建模所展示的部分。这部分是模型细节。不过agent和它们的环境联合任何的可选数据集合对象通常在这个方法里创建。当然,这个方法可能调用其它的方法来建模。SugarModel在buildModel()中用初始化参数构建了所有的Sugar Agents和Sugar Space。建立和收集数据的更多信息请看How To Collect Data

  2)private void buildDisplay()

  buildDisplay()方法创建了仿真中那些必须显示给用户的仿真部分。批处理模型可能没有这个方法。SugarModel在方法里为agent建立的display类和graphing类。但是DisplaySurface对象的实际创建应该发生在setup()方法中。创建displays的更多信息请看How To Create Displays,创建charts更多信息请看How to Create Charts

  3)private void buildSchedule()

  buildSchedule()方法创建了Schedule,负责改变仿真的状态。这就意味着schedule指明什么时候什么对象上调用什么方法。更多信息请看How to Use a Schedule。所有的示例模型的源码提供了很好的例子,Schedule API文档应该是按时间一直向前的。

  • Get and Set Accessor Methods

  首先是于模型如何通过SimInit类实例化,无论是作为命令行参数或通过它的main方法的背景。(利用SimInit启动仿真的更多信息请看How to Run a Simulation)。当一个非批处理的仿真通过SimInit类启动,一个Controller对象需要创建来控制模型的运行。如果模型的名称作为一个参数传递给SimInit,Controller在模型中调用getInitParam()并接受能显示并修改的初始化参数列表(当模型通过加载模型对话框加载的时候会执行同样的过程)。为了显示这些参数的值,controller判断模型是否为这些参数实现了get和set方法。如果实现了,controller调用get方法并给用户显示参数值。当用户通过图形化修改这些参数中的某一个时,相对应的set方法会被调用,新的值作为参数。一个典型的get和set访问器方法可能看起来像:

  .NET:

public void NumAgents() 
{
   set;
   get;
}

  JAVA:

public int getNumAgents() {
   return numAgents;
}


public void setNumAgents(int numAgents) {
   this.numAgents = numAgents;
}

  

  因此,如果用户想要显示初始化启动参数,并且使参数能修改,参数名必须通过getInitParam()方法返回,模型必须包含正确的get和set方法。例如,如果一个模型想要包含显示agent的数量的参数,这个参数也能修改,某个字符串如“numAgents”必须作为getInitParam的返回出现在数组中,模型必须有getNumAgents和 setNumAgents方法。仿真例子源码都遵循了这个原则,都是很好的例子。

  在仿真运行过程中的探测对象(agents,环境等)要求展示同样的访问模型。但是这种情况,对于显示的变量仅仅get方法需要。例如模型有一个变量suger,它跟踪着已经搜集的sugar的数量。设计仿真的人想要监测agent并且这个变量在仿真运行期间显示。为了变量的显示,agent必须要包含getSugar方法。如果agent没有set-Sugar方法,那么“Sugar”被认为是只读的并且命名为“Sugar”显示着。如果agent既有getSugar方法,也有setSugar方法,那么Sugar变量在仿真运行期间将能够被用户修改。

  在非模型对象上通过实现CustomProbeable接口你可以限制非模型对象(agents,环境等)中的哪个变量能被监测。CustomProbeable接口包含一个方法public String[] getProperties(),这个方法就像就像模型的getInitParams()方法。通过实现接口返回你想监测的属性字符串数组,你可以限制监测范围到那些属性。例如假定getProbedProperties返回“Sugar”和“Age”。监测机制方法只通过getSugar, setSugar and getAge, setAge方法寻找返回值。这个接口在你的agent继承其它类的属性不想显示的时候有用。在jiggle示例仿真中的椭圆agents就是很好的例子。

  通常情况下,仅仅Strings,booleans和numbers被监测。但是这不意味着仅仅这些类型的agent变量能成为检测源。从一种类型到另外一种类型的转换可以再set和get方法中进行。例如一个get方法可以遍历一个数组返回逗号分隔的字符串,set方法可以解析这个字符串放到一个数组中。hypercycle示例就是一个很好的例子。但是被监测的参数就是它自己一些非字符串、非数值型或者非布尔值得类,参数将作为一个按钮显示。点击这个按钮,然后监测参数自身在一个新的窗口中显示。

  参数的默认显示是文本框。如果你希望为布尔参数显示下拉选框或者复选框来限定一个选择范围,请参考How To Create Property Descriptors

  • Interface Required Methods

  为了实现SimModel接口,用户模型必须定义以下方法(如果这些方法不定义,模型无法通过编译):

  public String[] getInitParam() getInitParam应该返回用户想要显示和操作的初始化参数字符串数组。

  public void begin() begin初始化模型的启动运行。因此,那三个build方法在这里调用,任何显示信息都显示。不管什么时候点击start按钮,begin方法被调用(或者模型没有启动时点击step按钮)。所有的示例仿真实现了这个方法,是很好的例子。另外,你需要创建的任何对象所依赖的参数可以在这里创建(不是在setup中)。

  public void setup() setup 打乱了模型状态为调用begin()方法做准备。当模型加载时或是通过传递模型名称参数给SimInit,或是通过加载模型对话框,更多情况是是setup按钮被点击了。Setup应该设置所有被创建的执行的对象为空,销毁显示层、图和计划任务。 虽然不是绝对必要这有有助于阻止内存泄漏和确保干净的startup(调用System.gc()也能起到同样的效果)。

  初始化模型参数应该能设置让用户看到想看的初始状态,一个调度计划应该在这里创建(例如:schedule = new Schedule(1);),如果模型是一个用户界面接口模型,一个DisplaySurface也应该在这里创建(例如: displaySurface = new DisplaySurface(this,”Heat Bugs Display”);)。

依赖参数值的对象不能在这里创建或者设置。任何的参数操作或是通过用户接口或者是通过参数文件发生在setup()方法后的。作为一个结果你的对象需要用正确的参数值。

  public Schedule getSchedule() 返回了和模型相关的调度计划。通常这仅仅返回模型的调度计划变量。

  public String getName() 这应该返回模型的名称。这将作为工具栏的标题显示。

  不同的工具栏按钮和实际执行的代码的关系如下。当点击setup按钮时,setup()方法执行;当点击initialize按钮时,begin()方法执行;当点击step按钮时,begin()方法执行并且任何计划的下一刻的行为执行;当点击start按钮时,begin()方法执行,仿真随着时间一步步循环的执行计划中的行为直到用户点击了停止或者暂停按钮。

  • The Custom Actions Tab

  如果你创建了一个非批处理的图形化用户接口,你可以在设置窗口的Custom Actions选项卡中放置按钮,滑动窗口和单选框。你可以通过按钮等来改变模型、agent和环境等的状态在仿真运行的时候。示例仿真Heat Bugs和Hypercyclse就是这样做的很好的例子。更多地信息请看How to Create Custom Actions

  • Simulation Specific Methods

  一个模型可能具有仿真特殊的方法,例如计算和返回写入文件的数据EnnModel.java中的计算方法就是例子。

  • The Main Method

  一个模型可能包含可选的main方法。模型可能通过这个方法或通过命令行作为uchicago.src.sim.engine.SimInit 的参数启动。有关不同的方式启动Repast仿真的更多信息请看How To Run a Simulation

  如果你选择在你的模型中包含main方法,main方法在你的模型中创建一个SimInit实例,然后利用SimInit实例加载你的模型。例如:

public static void main(String[] args) {
  SimInit init = new SimInit();
  MyModel model = new MyModel();
  init.load(model, null, false);
}

  上边的用无参数的文件和gui模式加载Repast模型。但是你也能提出一个参数文件,在批处理方式下运行仿真。详细信息请看SimInit

批处理和非批处理模式都应该遵循这个大致的框架,尽管在批处理方式下buildDisplay方法不是必须的。

Batch Parameters

  set/get模式在批处理下的用法不同。就像一个非批处理模型的初始化参数能通过set和get方法显示和操作一样,批处理的模型用同的方法从参数文件中设置初始化参数。更多信息请看How To Use Parameters and Parameter Files