Repast Java:以结构化方式调度代理和全局行为
Repast Java: scheduling agent and global behaviors in a structural way
我以前使用 Netlogo 多年,我非常习惯基于一组过程开发基于代理的模型。供应链仿真模型结构示例如下:
;;the main simulation loop
@ScheduledMethod(start = 1, interval = 1)
public void step() {
place-order-to-suppliers() ;;procedures involving customer agent behaviors (a number of methods)
receive-shipment-from-suppliers() ;;procedures involving both supplier and customer agents and their behaviors (methods)
receive-order-from-customers() ;;procedures involving supplier agent only
ship-order-to-customers() ;;procedures involving supplier agent only
summarize() ;;procedures involving global summary behaviors independent of any agents, as well as local summary behaviors per each type of agents (customer and supplier)
}
以上结构对于开发仿真模型非常有用和直观。我们首先将模拟世界分成几个关键部分(过程),在这些部分中我们进一步开发与相关代理和行为相关的特定方法。关键部分是建立一个更高级别的程序(如程序包),这可能有助于将不同类型的代理及其 behaviors/interactions 一起集成(打包)在一个地方,并以基于所需的顺序执行模型在这些程序上。
有没有hints/examples在Repast中实现这样的模块化建模策略?
更新:
下面是我写的一个简单的模型,是关于男孩和女孩在聚会中如何互动的(完整的参考可以找到https://ccl.northwestern.edu/netlogo/models/Party)。下面是boyClass的代码(girl也是一样的就不贴了)
package party;
import java.util.ArrayList;
import java.util.List;
import repast.simphony.context.Context;
import repast.simphony.engine.environment.RunEnvironment;
import repast.simphony.engine.schedule.ScheduledMethod;
import repast.simphony.parameter.Parameters;
import repast.simphony.query.PropertyGreaterThan;
import repast.simphony.query.PropertyEquals;
import repast.simphony.query.Query;
import repast.simphony.random.RandomHelper;
import repast.simphony.space.continuous.ContinuousSpace;
import repast.simphony.space.grid.Grid;
import repast.simphony.space.grid.GridPoint;
import repast.simphony.util.ContextUtils;
public class Boy {
private ContinuousSpace<Object> space;
private Grid<Object> grid;
private boolean happy;
private int id, x, y,tolerance;
private boolean over;
Boy (Grid<Object> grid, int id, int x, int y) {
this.grid = grid;
this.id = id;
this.x = x;
this.y = y;
Parameters p = RunEnvironment.getInstance().getParameters();
int get_tolerance = (Integer) p.getValue("tolerance");
this.tolerance = get_tolerance;
}
// @ScheduledMethod(start = 1, interval = 1,shuffle=true)
// public void step() {
// relocation();
// update_happiness();
// endRun();
//
// }
public void endRun( ) {
Context<Object> context = ContextUtils.getContext(this);
Query<Object> query = new PropertyEquals<Object>(context, "happy", true);
int end_count = 0;
for (Object o : query.query()) {
if (o instanceof Boy) {
end_count ++;
}
if (o instanceof Girl) {
end_count ++;
}
}
if (end_count == 70) {
RunEnvironment.getInstance().endRun();
}
}
public void update_happiness() {
over = false;
Context<Object> context = ContextUtils.getContext(this);
Parameters p = RunEnvironment.getInstance().getParameters();
int tolerance = (Integer) p.getValue("tolerance");
GridPoint pt = grid.getLocation(this);
int my_x = this.getX();
int boy_count = 0;
int girl_count = 0;
Query<Object> query = new PropertyEquals<Object>(context, "x", my_x);
for (Object o : query.query()) {
if (o instanceof Boy) {
boy_count++;
}
else {
girl_count++;
}
}
int total = boy_count + girl_count;
double ratio = (girl_count / (double)total);
// System.out.println((girl_count / (double)total));
if (ratio <= (tolerance / (double)100)) {
happy = true;
// System.out.println("yes");
}
else {
happy = false;
// System.out.println("no");
}
over = true;
// System.out.println(over);
}
public void relocation() {
if (!happy) {
List<Integer> x_list = new ArrayList<Integer>();
for (int i = 5; i <= 50; i = i + 5) {
x_list.add(i);
}
int index = RandomHelper.nextIntFromTo(0, 9);
int group_x = x_list.get(index);
while(group_x == this.getX()){
index = RandomHelper.nextIntFromTo(0, 9);
group_x = x_list.get(index);
}
int group_y = 35;
while (grid.getObjectAt(group_x,group_y) != null) {
group_y = group_y + 1;
}
this.setX(group_x);
grid.moveTo(this, group_x,group_y);
}
}
public int getTolerance() {
return tolerance;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public int getID() {
return id;
}
public boolean getHappy() {
return happy;
}
public boolean getOver() {
return over;
}
public void setTolerance(int tolerance) {
this.tolerance = tolerance;
}
}
---------------------------------------------------------------------------------
以上代码的运行ning可以遵循标准的Repast Annotated调度方式。但是,由于我想对不同的代理及其方法进行一些集成,以允许创建更大的过程(方法),因此我设法创建了一个全局调度程序代理 Class 来管理此建模策略。下面是代码:
package party;
import java.util.ArrayList;
import java.util.List;
import repast.simphony.context.Context;
import repast.simphony.engine.environment.RunEnvironment;
import repast.simphony.engine.schedule.ScheduleParameters;
import repast.simphony.engine.schedule.ScheduledMethod;
import repast.simphony.engine.schedule.Schedule;
import repast.simphony.query.PropertyEquals;
import repast.simphony.query.Query;
import repast.simphony.util.ContextUtils;
import repast.simphony.util.collections.IndexedIterable;
public class Global_Scheduler {
@ScheduledMethod(start = 1, interval = 1,shuffle=true)
public void updateHappiness() {
Context<Object> context = ContextUtils.getContext(this);
IndexedIterable<Object> boy_agents = context.getObjects(Boy.class);
IndexedIterable<Object> girl_agents = context.getObjects(Girl.class);
for (Object b: boy_agents) {
((Boy) b).update_happiness();
}
for (Object g: girl_agents) {
((Girl) g).update_happiness();
}
}
@ScheduledMethod(start = 1, interval = 1,shuffle=true)
public void relocate() {
Context<Object> context = ContextUtils.getContext(this);
IndexedIterable<Object> boy_agents = context.getObjects(Boy.class);
IndexedIterable<Object> girl_agents = context.getObjects(Girl.class);
for (Object b: boy_agents) {
((Boy) b).relocation();
}
for (Object g: girl_agents) {
((Girl) g).relocation();
}
}
@ScheduledMethod(start = 1, interval = 1,shuffle=true)
public void summary() {
Context<Object> context = ContextUtils.getContext(this);
Query<Object> query = new PropertyEquals<Object>(context, "happy", true);
int total_count = 0;
int boy_count = 0;
int girl_count = 0;
for (Object o : query.query()) {
if (o instanceof Boy) {
total_count ++;
boy_count++;
}
if (o instanceof Girl) {
total_count ++;
girl_count++;
}
}
System.out.println("Total happy person: " + total_count);
System.out.println("Total happy boys: " + boy_count);
System.out.println("Total happy girls: " + girl_count);
}
@ScheduledMethod(start = 1, interval = 1,shuffle=true)
public void endRun( ) {
Context<Object> context = ContextUtils.getContext(this);
Query<Object> query = new PropertyEquals<Object>(context, "happy", true);
int end_count = 0;
for (Object o : query.query()) {
if (o instanceof Boy) {
end_count ++;
}
if (o instanceof Girl) {
end_count ++;
}
}
if (end_count == 70) {
RunEnvironment.getInstance().endRun();
}
}
}
以上代码使用全局调度程序代理 运行 模型工作正常,结果应该表现相同。但是,我不确定模型的执行是否真的遵循顺序(即 update_happiness() -> relocate() -> summary() -> end_run()。我也想知道有没有更好更简单的方法来实现这样的建模策略?
您提供的代码示例几乎可以像在就餐模型代理中一样工作——您只需更改注释行前缀;; // 并在代理 class 中实施方法 place-order-to-suppliers() 等。典型 ABM 中的代理行为结构完全遵循此结构。一种通用 'step' 方法,它根据所需的执行顺序组合各种子步骤。
就餐常见问题解答中概述了多种行为安排方法:https://repast.github.io/docs/RepastReference/RepastReference.html#_scheduling。如您在示例中提供的那样,通过注释进行调度将定期或在单个时间步长重复该行为。您还可以通过直接在 Repast 时间表上放置一个动作来在模型中动态安排。这种类型的调度适用于基于事件的行为,例如调度由模型中的其他事件触发的一次性行为。您还可以使用 @Watch 注释进行调度,这些注释会根据注释中指定的一组条件触发行为。
您可以在 @ScheduledMethod 注释中使用优先级,例如,
@ScheduledMethod(start = 1, interval = 1, shuffle=true, priority=1)
较高的优先级 运行 在较低的优先级之前。
我以前使用 Netlogo 多年,我非常习惯基于一组过程开发基于代理的模型。供应链仿真模型结构示例如下:
;;the main simulation loop
@ScheduledMethod(start = 1, interval = 1)
public void step() {
place-order-to-suppliers() ;;procedures involving customer agent behaviors (a number of methods)
receive-shipment-from-suppliers() ;;procedures involving both supplier and customer agents and their behaviors (methods)
receive-order-from-customers() ;;procedures involving supplier agent only
ship-order-to-customers() ;;procedures involving supplier agent only
summarize() ;;procedures involving global summary behaviors independent of any agents, as well as local summary behaviors per each type of agents (customer and supplier)
}
以上结构对于开发仿真模型非常有用和直观。我们首先将模拟世界分成几个关键部分(过程),在这些部分中我们进一步开发与相关代理和行为相关的特定方法。关键部分是建立一个更高级别的程序(如程序包),这可能有助于将不同类型的代理及其 behaviors/interactions 一起集成(打包)在一个地方,并以基于所需的顺序执行模型在这些程序上。
有没有hints/examples在Repast中实现这样的模块化建模策略?
更新: 下面是我写的一个简单的模型,是关于男孩和女孩在聚会中如何互动的(完整的参考可以找到https://ccl.northwestern.edu/netlogo/models/Party)。下面是boyClass的代码(girl也是一样的就不贴了)
package party;
import java.util.ArrayList;
import java.util.List;
import repast.simphony.context.Context;
import repast.simphony.engine.environment.RunEnvironment;
import repast.simphony.engine.schedule.ScheduledMethod;
import repast.simphony.parameter.Parameters;
import repast.simphony.query.PropertyGreaterThan;
import repast.simphony.query.PropertyEquals;
import repast.simphony.query.Query;
import repast.simphony.random.RandomHelper;
import repast.simphony.space.continuous.ContinuousSpace;
import repast.simphony.space.grid.Grid;
import repast.simphony.space.grid.GridPoint;
import repast.simphony.util.ContextUtils;
public class Boy {
private ContinuousSpace<Object> space;
private Grid<Object> grid;
private boolean happy;
private int id, x, y,tolerance;
private boolean over;
Boy (Grid<Object> grid, int id, int x, int y) {
this.grid = grid;
this.id = id;
this.x = x;
this.y = y;
Parameters p = RunEnvironment.getInstance().getParameters();
int get_tolerance = (Integer) p.getValue("tolerance");
this.tolerance = get_tolerance;
}
// @ScheduledMethod(start = 1, interval = 1,shuffle=true)
// public void step() {
// relocation();
// update_happiness();
// endRun();
//
// }
public void endRun( ) {
Context<Object> context = ContextUtils.getContext(this);
Query<Object> query = new PropertyEquals<Object>(context, "happy", true);
int end_count = 0;
for (Object o : query.query()) {
if (o instanceof Boy) {
end_count ++;
}
if (o instanceof Girl) {
end_count ++;
}
}
if (end_count == 70) {
RunEnvironment.getInstance().endRun();
}
}
public void update_happiness() {
over = false;
Context<Object> context = ContextUtils.getContext(this);
Parameters p = RunEnvironment.getInstance().getParameters();
int tolerance = (Integer) p.getValue("tolerance");
GridPoint pt = grid.getLocation(this);
int my_x = this.getX();
int boy_count = 0;
int girl_count = 0;
Query<Object> query = new PropertyEquals<Object>(context, "x", my_x);
for (Object o : query.query()) {
if (o instanceof Boy) {
boy_count++;
}
else {
girl_count++;
}
}
int total = boy_count + girl_count;
double ratio = (girl_count / (double)total);
// System.out.println((girl_count / (double)total));
if (ratio <= (tolerance / (double)100)) {
happy = true;
// System.out.println("yes");
}
else {
happy = false;
// System.out.println("no");
}
over = true;
// System.out.println(over);
}
public void relocation() {
if (!happy) {
List<Integer> x_list = new ArrayList<Integer>();
for (int i = 5; i <= 50; i = i + 5) {
x_list.add(i);
}
int index = RandomHelper.nextIntFromTo(0, 9);
int group_x = x_list.get(index);
while(group_x == this.getX()){
index = RandomHelper.nextIntFromTo(0, 9);
group_x = x_list.get(index);
}
int group_y = 35;
while (grid.getObjectAt(group_x,group_y) != null) {
group_y = group_y + 1;
}
this.setX(group_x);
grid.moveTo(this, group_x,group_y);
}
}
public int getTolerance() {
return tolerance;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public int getID() {
return id;
}
public boolean getHappy() {
return happy;
}
public boolean getOver() {
return over;
}
public void setTolerance(int tolerance) {
this.tolerance = tolerance;
}
}
---------------------------------------------------------------------------------
以上代码的运行ning可以遵循标准的Repast Annotated调度方式。但是,由于我想对不同的代理及其方法进行一些集成,以允许创建更大的过程(方法),因此我设法创建了一个全局调度程序代理 Class 来管理此建模策略。下面是代码:
package party;
import java.util.ArrayList;
import java.util.List;
import repast.simphony.context.Context;
import repast.simphony.engine.environment.RunEnvironment;
import repast.simphony.engine.schedule.ScheduleParameters;
import repast.simphony.engine.schedule.ScheduledMethod;
import repast.simphony.engine.schedule.Schedule;
import repast.simphony.query.PropertyEquals;
import repast.simphony.query.Query;
import repast.simphony.util.ContextUtils;
import repast.simphony.util.collections.IndexedIterable;
public class Global_Scheduler {
@ScheduledMethod(start = 1, interval = 1,shuffle=true)
public void updateHappiness() {
Context<Object> context = ContextUtils.getContext(this);
IndexedIterable<Object> boy_agents = context.getObjects(Boy.class);
IndexedIterable<Object> girl_agents = context.getObjects(Girl.class);
for (Object b: boy_agents) {
((Boy) b).update_happiness();
}
for (Object g: girl_agents) {
((Girl) g).update_happiness();
}
}
@ScheduledMethod(start = 1, interval = 1,shuffle=true)
public void relocate() {
Context<Object> context = ContextUtils.getContext(this);
IndexedIterable<Object> boy_agents = context.getObjects(Boy.class);
IndexedIterable<Object> girl_agents = context.getObjects(Girl.class);
for (Object b: boy_agents) {
((Boy) b).relocation();
}
for (Object g: girl_agents) {
((Girl) g).relocation();
}
}
@ScheduledMethod(start = 1, interval = 1,shuffle=true)
public void summary() {
Context<Object> context = ContextUtils.getContext(this);
Query<Object> query = new PropertyEquals<Object>(context, "happy", true);
int total_count = 0;
int boy_count = 0;
int girl_count = 0;
for (Object o : query.query()) {
if (o instanceof Boy) {
total_count ++;
boy_count++;
}
if (o instanceof Girl) {
total_count ++;
girl_count++;
}
}
System.out.println("Total happy person: " + total_count);
System.out.println("Total happy boys: " + boy_count);
System.out.println("Total happy girls: " + girl_count);
}
@ScheduledMethod(start = 1, interval = 1,shuffle=true)
public void endRun( ) {
Context<Object> context = ContextUtils.getContext(this);
Query<Object> query = new PropertyEquals<Object>(context, "happy", true);
int end_count = 0;
for (Object o : query.query()) {
if (o instanceof Boy) {
end_count ++;
}
if (o instanceof Girl) {
end_count ++;
}
}
if (end_count == 70) {
RunEnvironment.getInstance().endRun();
}
}
}
以上代码使用全局调度程序代理 运行 模型工作正常,结果应该表现相同。但是,我不确定模型的执行是否真的遵循顺序(即 update_happiness() -> relocate() -> summary() -> end_run()。我也想知道有没有更好更简单的方法来实现这样的建模策略?
您提供的代码示例几乎可以像在就餐模型代理中一样工作——您只需更改注释行前缀;; // 并在代理 class 中实施方法 place-order-to-suppliers() 等。典型 ABM 中的代理行为结构完全遵循此结构。一种通用 'step' 方法,它根据所需的执行顺序组合各种子步骤。
就餐常见问题解答中概述了多种行为安排方法:https://repast.github.io/docs/RepastReference/RepastReference.html#_scheduling。如您在示例中提供的那样,通过注释进行调度将定期或在单个时间步长重复该行为。您还可以通过直接在 Repast 时间表上放置一个动作来在模型中动态安排。这种类型的调度适用于基于事件的行为,例如调度由模型中的其他事件触发的一次性行为。您还可以使用 @Watch 注释进行调度,这些注释会根据注释中指定的一组条件触发行为。
您可以在 @ScheduledMethod 注释中使用优先级,例如,
@ScheduledMethod(start = 1, interval = 1, shuffle=true, priority=1)
较高的优先级 运行 在较低的优先级之前。