Repast Java: структурный агент планирования и глобального поведения

Раньше я работал с 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)
}

Приведенная выше структура очень полезна и интуитивно понятна для разработки имитационной модели. Сначала мы разрезаем мир моделирования на несколько ключевых частей (процедур), в рамках которых мы далее разрабатываем конкретные методы, связанные с соответствующими агентами и поведением. Существенной частью является создание процедуры более высокого уровня (например, пакета), которая может быть полезна для интеграции (упаковки) различных типов агентов и их поведения / взаимодействий в одном месте и выполнения модели в желаемом последовательном порядке на основе этих процедуры.

Есть ли какие-нибудь подсказки / примеры для реализации такой стратегии модульного моделирования в Repast?

Обновление: ниже я написал простую модель о том, как мальчик и девочка взаимодействуют на вечеринке (полную ссылку можно найти на https://ccl.northwestern.edu/netlogo/models/Party). Ниже приведен код для класса Boy (девочка такая же, поэтому не вставлялась снова).

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;
    }
}

---------------------------------------------------------------------------------

Выполнение приведенного выше кода может следовать стандартному методу планирования Repast Annotated. Однако, поскольку я хочу сделать некоторую интеграцию различных агентов и их методов в целом, чтобы позволить создание более крупных процедур (методов), мне удалось создать класс агента глобального планировщика для управления этой стратегией моделирования. Ниже приведен код:

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 (). Я также хотел бы знать, есть ли лучший и более простой способ достичь такой стратегии моделирования?


person Jack    schedule 26.08.2019    source источник


Ответы (2)


Вы можете использовать приоритеты в аннотациях @ScheduledMethod, например,

@ScheduledMethod(start = 1, interval = 1, shuffle=true, priority=1)

где более высокий приоритет будет выполняться перед более низким приоритетом.

person J. Ozik    schedule 03.09.2019
comment
благодаря. Есть ли у вас какие-либо советы по улучшению вышеуказанного метода создания и использования агента глобального планирования? - person Jack; 05.09.2019
comment
Для методов update_happiness и relocate вы можете просто аннотировать эти методы непосредственно в классе агента. Однако для сводки и endRun вы можете либо оставить его таким, либо создать действие в ContextBuilder и запланировать его программно (без аннотации). - person J. Ozik; 06.09.2019

Приведенный вами пример кода будет почти точно работать в агенте модели репаст - вам просто нужно изменить префикс строки комментария ;; // и реализовать методы place-order-to-suppliers () и т. д. в классе агента. Структура поведения агента в типичной ABM следует именно этой структуре. Общий «пошаговый» метод, объединяющий различные подэтапы в соответствии с желаемым порядком выполнения.

Существует ряд подходов к планированию поведения, описанных в FAQ по Repast: https://repast.github.io/docs/RepastReference/RepastReference.html#_scheduling. Планирование с помощью аннотации, как вы указали в примере, будет повторять поведение через регулярный интервал или за один временной шаг. Вы также можете динамически планировать в модели, напрямую помещая действие в расписание Repast. Этот тип планирования подходит для поведения, основанного на событиях, например для планирования одноразового поведения, которое запускается каким-либо другим событием в модели. Вы также можете запланировать с помощью аннотаций @Watch, которые запускают поведение на основе набора условий, указанных в аннотации.

person Eric Tatara    schedule 26.08.2019
comment
Большое спасибо за ваш ответ. Однако я обнаружил, что есть некоторые отличия. Методы в Repast почти разработаны для конкретного агента, который находится на нижнем уровне. То, что я проиллюстрировал выше, касается создания процедур более высокого уровня, которые могут интегрировать различные классы агентов и их поведение в одном месте и выполнять моделирование на основе порядка этих процедур. Это означает, что нам, вероятно, потребуется отдельный класс агента ModelRuning (аналогичный процедуре Netlogo Go) для реализации этих процедур в качестве основного цикла моделирования. - person Jack; 27.08.2019