本文翻译并提炼自官方文档
MASON的核心模拟代码在sim.engine
包中,该包主要包含以下类:
sim.engine.SimState
:保存Agent模型的全局对象。sim.engine.Schedule
:离散事件调度队列(Scheduling Queue)。sim.engine.Stepable
:由Agent实现的接口,step方法将作为Schedule的任务。- 其他工具类。
SimState模拟实体,Schedule模拟时间,而fields模拟空间和Agent之间的关系。fields相关类不在sim.engine
中,而在以下包中:
sim.field.grid
:保存二维或三维网格。sim.field.continuous
:保存连续的二维或三维网格数据。sim.field.network
:保存图形和网络。
(一)模型
启动主程序需要创建一个sim.engine.SimState
的子类,模拟所需的所有元素将组合在此类中。包括模型参数、自定义类型数据等,可以作为SimState
子类的一个成员变量保存。
由于模型中的所有数据最终都存储在SimState
子类的某个成员变量上,MASON可以将整个模型序列化为checkpoint文件,这意味着可以随时冻结模拟将其全部保存到文件中,并可以从此文件重新启动模型。为了实现这一点,所有MASON模型对象都实现了java.io.Serializable
,这意味着它们可以写入流或从流中读取。
SimState
已经包含了以下两个成员变量:
public MersenneTwisterFast random;
public Schedule schedule;
random
是采用Mersenne Twister算法实现的随机数快速生成器。与java.util.Random
的伪随机数不同,这是一个高质量的随机数生成器(尽管MersenneTwisterFast
也是伪随机数生成器)。
MersenneTrusterFast
不是线程同步的。通常MASON模拟是单线程的,也就没有问题。但如果模拟被分解为多个线程,需要确以线程安全的方式访问random
,示例代码如下:
double val = 0;
// state是SimState的实例
synchronized(state.random) {
val = random.nextDouble();
}
schedule
将在下文介绍。
(二)循环
MASON通过SimState
来进行模拟,它加载设定好步骤的Schedule
,重复执行时间表直到没有step可执行,然后清理。具体步骤如示例代码所示:
public static void main(String[] args) {
int jobs = 100; // 迭代次数上限
// 创建SimState子类的实例,System.currentTimeMillis()将作为随机数生成器的种子
SimState state = new MyModel(System.currentTimeMillis());
for(int job = 0; job < jobs; job++) {
state.setJob(job);
state.start();
do
if (!state.schedule.step(state)) break;
while(state.schedule.getSteps() < 5000);
state.finish();
}
// 退出,需要终止所有可能意外创建的线程
System.exit(0);
}
doLoop()方法
如果从命令行运行模型(没有GUI),有一种更简单的方法:使用SimState
的doLoop
方法:
public static void main(String[] args) {
doLoop(MyModel.class, args);
System.exit(0);
}
doLoop
的优点是它提供了许多命令行工具:
- 工作处理。运行模拟指定次,并可以将它们放入并行批中处理。
- 检查点。保存检查点并从检查点文件启动。
- 指定随机数种子。
- 指定模拟时长或步长。
- 运行时自动打印时间戳,以便确定模型运行了多久。
完整参数作用可在启动SimState
时追加-help
参数查看。
java MyModel -help
启动和完成
模拟开始时,首先调用(构造函数除外)start()
。结束时最后调用finish()
。其定义如下:
// 在迭代之前立即调用。可能需要重写此方法来设置模拟(或者清理并重新准备)。
// 一定要先调用super.start()
public void start();
// schedule用完后立即调用。您可能希望覆盖此方法以在以后进行清理:例如关闭各种流。
// 一定要先调用super.finish()
public void finish();
通常需要重写的方法是start()
,一般覆写需要完成以下任务:
- 重置全局变量以再次使用;
- 用初始agent重载schedule,schedule将会在
super.start()
中被清除。