Spatial and Netlogo like Models

ABCE deliberately does not provide spatial representation, instead it integrates with other packages that specialize in spatial representation.

Netlogo like models

For Netlogo like models in Python, we recommend using ABCE together with MESA

A simple example shows how to build a spatial model in ABCE using MESA:

On github

A wrapper file to start the graphical representation and the simulation

""" This is a simple demonstration model how to integrate ABCE and mesa.
The model and scheduler specification are taken care of in
ABCE instead of Mesa.

Based on
https://github.com/projectmesa/mesa/tree/master/examples/boltzmann_wealth_model.

For further reading, see
[Dragulescu, A and Yakovenko, V. Statistical Mechanics of Money, Income, and Wealth: A Short Survey. November, 2002](http://arxiv.org/pdf/cond-mat/0211175v1.pdf)
"""
from model import MoneyModel
from mesa.visualization.modules import CanvasGrid
from mesa.visualization.ModularVisualization import ModularServer
from mesa.visualization.modules import ChartModule


def agent_portrayal(agent):
    """ This function returns a big red circle, when an agent is wealthy and a
    small gray circle when he is not """
    portrayal = {"Shape": "circle",
                 "Filled": "true",
                 "r": 0.5}

    if agent.report_wealth() > 0:
        portrayal["Color"] = "red"
        portrayal["Layer"] = 0
    else:
        portrayal["Color"] = "grey"
        portrayal["Layer"] = 1
        portrayal["r"] = 0.2
    return portrayal


def main(x_size, y_size):
    """ This function sets up a canvas to graphically represent the model 'MoneyModel'
    and a chart, than it runs the server and runs the model in model.py in the browser """
    grid = CanvasGrid(agent_portrayal, x_size, y_size, 500, 500)

    chart = ChartModule([{"Label": "Gini",
                          "Color": "Black"}],
                        data_collector_name='datacollector')
    # the simulation uses a class DataCollector, that collects the data and
    # relays it from self.datacollector to the webpage

    server = ModularServer(MoneyModel,
                           [grid, chart],
                           "ABCE and MESA integrated",
                           x_size * y_size, x_size, y_size)
    server.port = 8534  # change this number if address is in use
    server.launch()


if __name__ == '__main__':
    main(25, 25)

A file with the simulation itself, that can be executed also without the GUI

""" This is a simple demonstration model how to integrate ABCE and mesa.
The model and scheduler specification are taken care of in
ABCE instead of Mesa.

Based on
https://github.com/projectmesa/mesa/tree/master/examples/boltzmann_wealth_model.

For further reading, see
[Dragulescu, A and Yakovenko, V. Statistical Mechanics of Money, Income, and Wealth: A Short Survey. November, 2002](http://arxiv.org/pdf/cond-mat/0211175v1.pdf)
"""
import abce
from mesa.space import MultiGrid
from mesa.datacollection import DataCollector
from moneyagent import MoneyAgent


def compute_gini(model):
    """ calculates the index of wealth distribution form a list of numbers """
    agent_wealths = model.wealths
    x = sorted(agent_wealths)
    N = len(agent_wealths)
    B = sum(xi * (N - i) for i, xi in enumerate(x)) / (N * sum(x))
    return 1 + (1 / N) - 2 * B


class MoneyModel(abce.Simulation):  # The actual simulation must inherit from Simulation
    """ The actual simulation. In order to interoperate with MESA the simulation
    needs to be encapsulated in a class. __init__ sets the simulation up. The step
    function runs one round of the simulation. """

    def __init__(self, num_agents, x_size, y_size):
        abce.Simulation.__init__(self,
                                 name='ABCE and MESA integrated',
                                 rounds=300,
                                 processes=1)
        # initialization of the base class. MESA integration requires
        # single processing
        self.grid = MultiGrid(x_size, y_size, True)
        self.agents = self.build_agents(MoneyAgent, 'MoneyAgent', num_agents,
                                        parameters={'grid': self.grid})
        # ABCE agents must inherit the MESA grid
        self.running = True
        # MESA requires this
        self.datacollector = DataCollector(
            model_reporters={"Gini": compute_gini})
        # The data collector collects a certain aggregate value so the graphical
        # components can access them

        self.wealths = [0 for _ in range(num_agents)]

    def step(self):
        """ In every step the agent's methods are executed, every set the round
        counter needs to be increased by self.next_round() """
        self.next_round()
        self.agents.do('move')
        self.agents.do('give_money')
        self.wealths = self.agents.do('report_wealth')
        # agents report there wealth in a list self.wealth
        self.datacollector.collect(self)
        # collects the data


if __name__ == '__main__':
    """ If you run model.py the simulation is executed without graphical
    representation """
    money_model = MoneyModel(1000, 20, 50)
    for r in range(100):
        print(r)
        money_model.step()

A simple agent

import abce
import random


class MoneyAgent(abce.Agent):
    """ agents move randomly on a grid and give_money to another agent in the same cell """

    def init(self, parameters, agent_parameters):
        self.grid = parameters["grid"]
        """ the grid on which agents live must be imported """
        x = random.randrange(self.grid.width)
        y = random.randrange(self.grid.height)
        self.pos = (x, y)
        self.grid.place_agent(self, (x, y))
        self.create('money', random.randrange(2, 10))

    def move(self):
        """ moves randomly """
        possible_steps = self.grid.get_neighborhood(self.pos,
                                                    moore=True,
                                                    include_center=False)
        new_position = random.choice(possible_steps)
        self.grid.move_agent(self, new_position)

    def give_money(self):
        """ If the agent has wealth he gives it to cellmates """
        cellmates = self.grid.get_cell_list_contents([self.pos])
        if len(cellmates) > 1:
            other = random.choice(cellmates)
            try:
                self.give(other.group, other.id, good='money', quantity=1)
            except abce.NotEnoughGoods:
                pass

    def report_wealth(self):
        return self.possession('money')