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)

较高的优先级 运行 在较低的优先级之前。