Среда проектирования агентных моделей для суперкомпьютеров — RepastHPC

В Аргоннской национальной лаборатории разработано программное обеспечение для проектирования агент-ориентированных моделей с целью последующего запуска на суперкомпьютерах, — Repast for High Performance Computing (RepastHPC). Данный пакет реализован с использованием языка C++ и MPI — программного интерфейса для обмена сообщениями между процессами, выполняющими задачу в параллельном режиме, а также библиотеки Boost, расширяющей C++.
В рамках RepastHPC реализован динамический дискретно-событийный планировщик выполнения программных инструкций с консервативными алгоритмами синхронизации, предусматривающими задержку процессов для соблюдения определенной очередности их выполнения.
RePast1.jpg

Рис. 1. RepastHPC многократно тестировался в Аргоннской национальной лаборатории на суперкомпьютере IBM Blue Gene/P

 

В RepastHPC агенты распределяются между процессами, и каждый процесс связан с агентом, являющимся локальным по отношению к данному процессу. В свою очередь агент локален к процессу, выполняющему программный код, описывающий поведение данного агента. При этом копии остальных — нелокальных — агентов могут присутствовать в любом процессе, что позволяет агентам всей модели взаимодействовать с этими копиями. К примеру, пусть пользователь в своей модели, предполагающей параллельные вычисления, использует два процесса — P1 и P2, каждый из которых создает определенное количество агентов и имеет собственный планировщик выполнения программных инструкций. Агенты, поведение которых рассчитывается на процессе P1, являются локальными по отношению к данному процессу, и только в рамках данного процесса программный код может изменить их состояние (аналогично и для процесса P2). Предположим, процесс P1 запрашивает копию агента A2 из процесса P2. Агент A2 не является локальным по отношению к процессу P1, и соответственно программный код, выполняемый в рамках процесса P1, не может изменить состояние агента A2. При этом агенты, реализуемые в рамках процесса P1, при необходимости могут запросить состояние агента A2, но копия A2 останется неизменной. Изменение оригинального A2 возможно только в рамках процесса P2, но в этом случае RepastHPC синхронизирует изменения состояния агента между всеми процессами.
Пример агентной модели в RepastHPC
Как правило, для реализации агентной модели в рамках RepastHPC требуются: 1) классы объектов типа «Агент» (листинг 1); 2) класс верхнего уровня — Model; 3) функция main.

class Agent 
{ 
public: 
    virtual ~Agent() {}

    virtual AgentId& getId() = 0;
    virtual const AgentId& getId() const = 0;
};

 

Листинг 1. Пример интерфейса класса «Агент»

В листинге 2 создается конструктор класса с единственной переменной _state типа int, которая определяет состояние агента. Естественно, в моделях агенты обычно имеют более сложную структуру.

class ModelAgent : public repast::Agent 
{
private:
    repast::AgentId _id;
    int _state;

public:
    ModelAgent(repast::AgentId id, int state);
    virtual ~ModelAgent();

    int state() const
    {
        return _state;
    }

    void state(int val)
    {
        _state = val;
    }

    repast::AgentId& getId()
    {
        return _id;
    }

    const repast::AgentId& getId() const
    {
        return _id;
    }

    void flipState(); 
};

Листинг 2. Конструктор класса, определяющего состояние агента

Далее создается структура, позволяющая скопировать агент от одного процесса к другому (листинг 3).

 

struct ModelAgentPackage 
{ 
    friend class boost::serialization::access;

    template <class Archive>
    void serialize(Archive& ar, const unsigned int version)
    {
        ar & id;
        ar & state;
    }

    repast::AgentId id;
    int state;

    repast::AgentId getId() const
    {
        return id;
    } 
};

 

Листинг 3. Пример структуры, осуществляющей копирования агента между процессами

Класс верхнего уровня Model нужен для начальной инициализации модели и для старта симуляции. Как правило, в рамках данного класса осуществляется распределение агентов по процессам, а также создается среда для функционирования агентов (решетка или непрерывное пространство). Кроме того, происходит инициализация данных для модели и планирование расписания для синхронизации. В листинге 4 приведен пример определения класса Model.

 

class Model
{
private:
    int rank;

public:
    repast::SharedContext<ModelAgent> agents;
    repast::SharedNetwork<ModelAgent, ModelEdge>* net;
    repast::SharedGrids<ModelAgent>::SharedWrappedGrid* grid;
    repast::DataSet* dataSet;

    Model();
    virtual ~Model();
    void initSchedule();
    void step();
};

 

Листинг 4. Инициализация модели (пример класса Model)

В листинге 5 создается конструктор класса Model. Сначала определяется ранг процессов, в рамках которых выполняются симуляции, а затем в качестве примера создаются 4 агента с уникальным номером и определением обслуживающего процесса. Также определяется среда для взаимодействия агентов — решетка размерностью 40×60 и назначается источник данных.

 

const MODEL_AGENT_TYPE = 0;

Model::Model()
{
    rank = RepastProcess::instance()->rank();

    for (int i = 0; i < 4; i++)
    {
        AgentId id(i, rank, MODEL_AGENT_TYPE);
        agents.addAgent(new ModelAgent(id, rank));
    }

    net = new SharedNetwork<ModelAgent, ModelEdge>("network", true);
    agents.addProjection(net);

    grid = new SharedGrids<ModelAgent>::SharedWrappedGrid("grid", 
        GridDimensions(Point(40, 60)), std::vector(2, 2), 2);
    agents.addProjection(grid);

    NCDataSetBuilder builder("./output/data.ncf", 
        RepastProcess::instance()->getScheduleRunner().schedule());

    StateSum* sum = new StateSum(this);
    builder.addDataSource(repast::createNCDataSource("state_sum", sum, std::plus<>()));

    dataSet = builder.createDataSet();

    Provider provider(this);
    AgentsCreator creator(this);

    grid->synchBuffer<ModelPackage>(agents, provider, creator);
}

 

Листинг 5. Пример конструктора класса Model

В листинге 6 приведен пример инициализации планировщика. Как видно, остановка происходит на 2000-м шаге, но при этом на каждом шаге вызывается метод step, а также осуществляется изменение состояния агентов и запись данных.

void Model::initSchedule()
{
    ScheduleRunner& runner = RepastProcess::instance()->getScheduleRunner();

    runner.scheduleStop(2000);

    runner.scheduleEvent(1, 1, Schedule::FunctorPtr(new MethodFunctor(this, &Model::step)));
    runner.scheduleEvent(1.1, 1, Schedule::FunctorPtr(new MethodFunctor<DataSet>(dataSet, &DataSet::record)));

    Schedule::FunctorPtr dsWrite = Schedule::FunctorPtr(new MethodFunctor<DataSet>(dataSet, &DataSet::write));
    runner.scheduleEvent(25.2, 25, dsWrite);

    runner.scheduleEndEvent(dsWrite);
}

 

Листинг 6. Инициализация планировщика модели

И наконец, функция main обычно осуществляет инициализацию MPI в рамках RepastHPC, создает класс Model и запускает планировщик модели (листинг 7).

int main(int argc, char** argv)
{
    mpi::environment env(argc, argv);
    
    std::string config = argv[1];
    std::string propsfile = argv[2];
    
    try
    {
        RepastProcess::init(config);
        Properties props(propsfile);
        repast::initializeRandom(props);

        Model model();
        model.initSchedule();

        ScheduleRunner& runner = RepastProcess::instance()->getScheduleRunner();
        runner.run();
    }
    catch (std::exception& ex)
    {
        std::cerr << "Error while running the rumor model: " << ex.what() << std::endl;
        throw ex;
    }

    RepastProcess::instance()->done();
    return 0;
}

 

Листинг 7. Пример функции main

Более подробно с данным ПО можно познакомиться в руководстве пользователя

[Collier Nick (2012): Repast HPC Manual, February 23, 2012]

, а по этому адресу можно скачать версию пакета 1.0.1 (по состоянию на 5 марта 2012 г.).

Агент-ориентированность Искусственные общества Суперкомпьютеры