Interactive jupyter notebook Tutorial

You will learn how to:

Create Simulation & Agents

Run a simulation and advance time

Give Goods & Trade

Capturing Data and Pandas

Communication between Simulation and Agents

jupyter_tutorial

Interactive jupyter / IPython notebook Tutorial

This tutorial works on jupyter notebook. Please download examples from here: https://github.com/AB-CE/examples and load the jupyter_tutorial by changing in the examples/jupyter_tutorial folder and typing jupyter notebook jupyter_tutorial.ipynb. Then you can execute the notebook cell by cell and observe abcEconomics at work.

In [1]:
import abcEconomics
/Users/taghawi/Dropbox/workspace/abcEconomics/pypi093b0/lib/python3.6/site-packages/bokeh/util/deprecation.py:34: BokehDeprecationWarning: 
The bokeh.charts API has moved to a separate 'bkcharts' package.

This compatibility shim will remain until Bokeh 1.0 is released.
After that, if you want to use this API you will have to install
the bkcharts package explicitly.

  warn(message)
In [2]:
simulation = abcEconomics.Simulation(name='ipythonsimulation', processes=1)

Creating the Agent

We create a simple agent and intialize it. An agent is an object that has propeties and does things. In python that is a class. We create an agent that has a name, knows the world size and can say his name.

The agent needs to inherit from abcEconomics.Agents. The __init__ function should not be overwritten, instead create an init function. The init function will be called during initialisation. The agents gets two parameters. All agents get the parameters we will specify in the next round. From agent_parameters the agents get only agent_parameters[id], where id is the number of the agent. Agents have for now only one function. They can say their name.

In [3]:
class Agent(abcEconomics.Agent):
    def init(self, world_size, family_name):
        self.family_name = family_name
        self.world_size = world_size
        print(world_size)
        
    def say(self):
        print("hello I am %s my id %i and my group is '%s', it is the %i round" % (self.family_name, self.id, self.group, self.round))

From the abstract agent, we create four concrete agents.

In [4]:
agents = simulation.build_agents(Agent, 'agent',
                                 world_size=30,
                                 agent_parameters=[{'family_name': 'fred'}, 
                                                   {'family_name': 'astaire'}, 
                                                   {'family_name': 'altair'}, 
                                                   {'family_name': 'deurich'}])
30
30
30
30

agents allows you to call each agents in the agents group. Each agent in this group has a group name, which is 'agent' and an id. Further we give each of the agents a parameter a family_name. We can now run the simulation and let all agents say their name:

In [5]:
for r in range(5):
    simulation.advance_round(r)
    agents.say()
Round0
hello I am fred my id 0 and my group is 'agent', it is the 0 round
hello I am astaire my id 1 and my group is 'agent', it is the 0 round
hello I am altair my id 2 and my group is 'agent', it is the 0 round
hello I am deurich my id 3 and my group is 'agent', it is the 0 round
Round1
hello I am fred my id 0 and my group is 'agent', it is the 1 round
hello I am astaire my id 1 and my group is 'agent', it is the 1 round
hello I am altair my id 2 and my group is 'agent', it is the 1 round
hello I am deurich my id 3 and my group is 'agent', it is the 1 round
Round2
hello I am fred my id 0 and my group is 'agent', it is the 2 round
hello I am astaire my id 1 and my group is 'agent', it is the 2 round
hello I am altair my id 2 and my group is 'agent', it is the 2 round
hello I am deurich my id 3 and my group is 'agent', it is the 2 round
Round3
hello I am fred my id 0 and my group is 'agent', it is the 3 round
hello I am astaire my id 1 and my group is 'agent', it is the 3 round
hello I am altair my id 2 and my group is 'agent', it is the 3 round
hello I am deurich my id 3 and my group is 'agent', it is the 3 round
Round4
hello I am fred my id 0 and my group is 'agent', it is the 4 round
hello I am astaire my id 1 and my group is 'agent', it is the 4 round
hello I am altair my id 2 and my group is 'agent', it is the 4 round
hello I am deurich my id 3 and my group is 'agent', it is the 4 round

It is necessary to tell the simulation when a new round starts. The parameter can be any representation of time.

Giving a Good

abcEconomics provide goods. Goods are things that can be given, sold or transformed. We create 5 agents, the first one has a balls the agents pass the ball around.

In [6]:
class Kid(abcEconomics.Agent):
    def init(self, num_kids):
        self.num_kids = num_kids
        if self.id == 0:
            self.create('ball', 1)
        
    def whether_I_have_the_ball(self):
        if self['ball'] > 0:
            print('*', end="", flush=True)
        else:
            print('.', end="", flush=True)
            
    def give_the_ball_to_the_next_kid(self):
        next_kid = (self.id + 1) % self.num_kids  # the id of the next kid, if I am the last the first kid
        if self['ball'] >= 1:
            self.give(('kid', next_kid), good='ball', quantity=1)
            
    
        

self.create, creates an object. self.possession, checks how much of one object an agent has. self.give, gives an object to another agent, specied by its group name and its id.

In [7]:
num_kids = 5
In [8]:
simulation = abcEconomics.Simulation(name='ipythonsimulation', processes=1)
In [9]:
kids = simulation.build_agents(Kid, 'kid', number=num_kids,
                               num_kids=num_kids) 
                            

When agent_parameters is not specified the numer of agents to be created needs to be spezified

In [10]:
for r in range(7):
    simulation.advance_round(r)
    kids.whether_I_have_the_ball()
    print()
    kids.give_the_ball_to_the_next_kid()
Round0
*....
Round1
.*...
Round2
..*..
Round3
...*.
Round4
....*
Round5
*....
Round6
.*...

Trade

In [11]:
from random import randrange

Well in every school yard we have a drug dealer.

In [12]:
class NewKid(abcEconomics.Agent):
    def init(self, num_dealers):
        self.num_dealers = num_dealers
        self.create('money', 100)  # don't we all wish you'd this function in real live?
        
    def buy_drugs(self):
        drug_dealer_id = randrange(self.num_dealers)
        self.buy(('drug_dealer', drug_dealer_id), good='drugs', quantity=1, price=10)
    
    def print_possessions(self):
        print('    ' + self.group + str(dict(self.possessions())))
        
    

The new kids, approach a random drug dealer and offer him 10 bucks.

In [13]:
class DrugDealer(abcEconomics.Agent):
    def init(self):
        self.create('drugs', 1)
        
    def sell_to_customers(self):
        for offer in self.get_offers('drugs'):
            if offer.price >= 10 and self['drugs'] >= 1:
                self.accept(offer)
    
    def print_possessions(self):
        print('    ' + self.group + str(dict(self.possessions())))

Drug dealer look at all the sell offers they get and decide to sell only to those kids that are willing to give them at least 10 dollars.

In [14]:
num_dealers = 1
In [15]:
simulation = abcEconomics.Simulation(name='school_yard', processes=1)
In [16]:
drug_dealers = simulation.build_agents(DrugDealer, 'drug_dealer', number=num_dealers)
In [17]:
customers = simulation.build_agents(NewKid, 'customer', number=1, 
                                   num_dealers=num_dealers)

builds 1 drug dealer and one customer.

In [18]:
kids = drug_dealers + customers

Groups of agents can be merged to 'super' groups. We will print the amount of drugs and money all kids have for each of the two kids

In [19]:
for r in range(2):
    simulation.advance_round(r)
    print('Customer offers 10 dollar:')
    customers.buy_drugs()
    kids.print_possessions()
    print('Drug Dealer accepts or rejects the offer:')
    drug_dealers.sell_to_customers()
    kids.print_possessions()
    print()
Round0
Customer offers 10 dollar:
    drug_dealer{'drugs': 1.0}
    customer{'money': 90.0}
Drug Dealer accepts or rejects the offer:
    drug_dealer{'drugs': 0}
    customer{'money': 90.0, 'drugs': 1.0}

Round1
Customer offers 10 dollar:
    drug_dealer{'drugs': 0}
    customer{'money': 80.0, 'drugs': 1.0}
Drug Dealer accepts or rejects the offer:
    drug_dealer{'drugs': 1.0}
    customer{'money': 90.0, 'drugs': 1.0}

When looking at round one, one can see that after the customer offered 10 dollars, the 10 dollars are not available to the dealer until the deal has either been accepted or rejected. After the drug dealer accepts the offer in the 0 round, the money is transfered to the drug dealer and the drugs to the customer.

In round 1, where the drug dealer runs out of drugs the 10 dollars go back to the customer.

Lets capture data

There are three ways of capturing data. aggregate and panel collect data from a specified group at a specified point of time. This has the advantage that there is no logging code in the agent class. self.log('name', value) saves a value under a certain name.

In [20]:
from math import sin

class DataDealer(abcEconomics.Agent):
    def init(self):
        self.count = 0
        self.create('money', 0)

    def counting(self):
        self.count += 1
        self.curve = sin(self.count / 100)
        self.create('money', self.curve * self.id)
        
        
In [21]:
simulation = abcEconomics.Simulation(name='gatherdata', processes=1)

It is specified which agents group collects which variables and possessions.

In [22]:
datadealers = simulation.build_agents(DataDealer, 'datadealer', number=10)

Every round the groups need to be instructed to collect the according data. simulation.finalize() must be called after the simulation, to write the data! Otherwise the program hangs.

In [23]:
for r in range(100):
    simulation.advance_round(r)
    datadealers.counting()
    datadealers.agg_log(variables=['count'])
    datadealers.panel_log(goods=['money'], variables=['curve'])
simulation.finalize()    
        
time only simulation   2.27
Round0
Round1
Round2
Round3
Round4
Round5
Round6
Round7
Round8
Round9
Round10
Round11
Round12
Round13
Round14
Round15
Round16
Round17
Round18
Round19
Round20
Round21
Round22
Round23
Round24
Round25
Round26
Round27
Round28
Round29
Round30
Round31
Round32
Round33
Round34
Round35
Round36
Round37
Round38
Round39
Round40
Round41
Round42
Round43
Round44
Round45
Round46
Round47
Round48
Round49
Round50
Round51
Round52
Round53
Round54
Round55
Round56
Round57
Round58
Round59
Round60
Round61
Round62
Round63
Round64
Round65
Round66
Round67
Round68
Round69
Round70
Round71
Round72
Round73
Round74
Round75
Round76
Round77
Round78
Round79
Round80
Round81
Round82
Round83
Round84
Round85
Round86
Round87
Round88
Round89
Round90
Round91
Round92
Round93
Round94
Round95
Round96
Round97
Round98
Round99

time only simulation   0.39
time with data   2.32
{
    "name": "ipythonsimulation",
    "random_seed": 1519701315.8717241,
    "num_kids": 5
}
time with data   0.55
{
    "name": "gatherdata",
    "random_seed": 1519701317.7969768
}

We can find the directory of the simulation data by using the simulation.path property

In [24]:
print(simulation.path)
/Users/taghawi/Dropbox/workspace/abcEconomics_examples/examples/jupyter_tutorial/result/gatherdata_2018-02-27_04-15

In that directory are the data files and a describtion.txt

In [25]:
import os
os.listdir(simulation.path)
Out[25]:
['panel_datadealer.csv',
 'aggregated_datadealer.csv',
 'aggregate_datadealer.csv',
 'description.txt']

Using statistical software

In [26]:
import pandas as pd
In [27]:
%matplotlib inline
In [28]:
df = pd.read_csv(simulation.path + '/panel_datadealer.csv')
In [29]:
df.head(20)
Out[29]:
index curve money round id
0 1 0.010000 0.000000 0 0
1 2 0.010000 0.010000 0 1
2 3 0.010000 0.020000 0 2
3 4 0.010000 0.030000 0 3
4 5 0.010000 0.039999 0 4
5 6 0.010000 0.049999 0 5
6 7 0.010000 0.059999 0 6
7 8 0.010000 0.069999 0 7
8 9 0.010000 0.079999 0 8
9 10 0.010000 0.089999 0 9
10 11 0.019999 0.000000 1 0
11 12 0.019999 0.029999 1 1
12 13 0.019999 0.059997 1 2
13 14 0.019999 0.089996 1 3
14 15 0.019999 0.119994 1 4
15 16 0.019999 0.149993 1 5
16 17 0.019999 0.179991 1 6
17 18 0.019999 0.209990 1 7
18 19 0.019999 0.239988 1 8
19 20 0.019999 0.269987 1 9
In [30]:
df.pivot(index='round', columns='id', values='money').plot()
Out[30]:
<matplotlib.axes._subplots.AxesSubplot at 0x114f3b7b8>

Communication between Simulation and Agents

In [31]:
simulation = abcEconomics.Simulation()
In [32]:
class Communicator(abcEconomics.Agent):
    def report_my_name(self):
        return self.name
    
    def do_as_told(self, time, text):
        print("it is %i o'clock and I have to say %s" % (time, text))
In [33]:
communicators = simulation.build_agents(Communicator, 'communicator', number=3)
Warning: agent communicator has no init function
Warning: agent communicator has no init function
Warning: agent communicator has no init function
In [34]:
for time in range(3):
    simulation.advance_round(time)
    communicators.report_my_name()
    communicators.do_as_told(time=time, text="Right says fred")
    
    
Round0
it is 0 o'clock and I have to say Right says fred
it is 0 o'clock and I have to say Right says fred
it is 0 o'clock and I have to say Right says fred
Round1
it is 1 o'clock and I have to say Right says fred
it is 1 o'clock and I have to say Right says fred
it is 1 o'clock and I have to say Right says fred
Round2
it is 2 o'clock and I have to say Right says fred
it is 2 o'clock and I have to say Right says fred
it is 2 o'clock and I have to say Right says fred

One caviat is that only keyword arguments work.