├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── examples
├── 2sectors
│ ├── firm.py
│ ├── household.py
│ └── start.py
├── 50000_firms
│ ├── b.py
│ ├── myagent.py
│ ├── start.py
│ ├── start.py.lprof
│ └── youragent.py
├── calendar
│ ├── agent.py
│ └── start.py
├── cce
│ ├── climate_square.sam.csv
│ ├── coal_produced_comment.txt
│ ├── firm.py
│ ├── government.py
│ ├── hirachical_taxes_nx.sam.csv
│ ├── household.py
│ ├── household_welfare.txt
│ ├── intro.txt
│ ├── iotable.py
│ ├── netexport.py
│ ├── optimization_functions.py
│ ├── sam_to_functions.py
│ └── start.py
├── create_and_delete_agents
│ ├── ball.py
│ ├── killer.py
│ ├── start.py
│ └── victim.py
├── jupyter_tutorial
│ └── jupyter_tutorial.ipynb
├── mesa_example
│ ├── model.py
│ ├── moneyagent.py
│ └── start.py
├── monetary_shocks
│ ├── centralbank.py
│ ├── firm.py
│ ├── graphs.py
│ ├── household.py
│ ├── optimization_functions.py
│ ├── publication_graphs.py
│ ├── start.py
│ ├── systematic_graph.py
│ └── systematic_household_graph.py
├── one_household_one_firm_with_logic
│ ├── firm.py
│ ├── household.py
│ └── start.py
├── pid_controller
│ ├── analytical
│ │ ├── agents_parameters.csv
│ │ ├── firm.py
│ │ ├── market.py
│ │ ├── picontroller.py
│ │ ├── simulation_parameters.csv
│ │ └── start.py
│ ├── full_pi_controlled
│ │ ├── firm.py
│ │ ├── labormarket.py
│ │ ├── market.py
│ │ ├── picontroller.py
│ │ ├── start.py
│ │ └── upregression.py
│ └── simple_seller_example
│ │ ├── firm.py
│ │ ├── household.py
│ │ ├── print_profile.py
│ │ ├── profile.sh
│ │ ├── simulation_parameters.csv
│ │ └── start.py
└── sugarscape
│ ├── agents.py
│ ├── start.py
│ └── sugar-map.txt
└── requirements.txt
/.gitignore:
--------------------------------------------------------------------------------
1 | *.pyc
2 | build/*
3 | dist/*
4 | **/result/*
5 | *.egg-info/*
6 | *.so
7 | *.pyd
8 | .dropbox
9 | abce/build/
10 | docs/_build/
11 | docs/builddir/
12 | **__pycache__*
13 | *.db
14 |
15 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | dist: trusty
2 | language: python
3 | notifications:
4 | email: false
5 | cache:
6 | pip: true
7 | matrix:
8 | include:
9 | - python: 3.6
10 | install:
11 | - pip install -r requirements.txt
12 | #- pip install mesa # for mesa_example
13 | - pip install flake8
14 |
15 | script:
16 | - flake8 examples --ignore=F403,E501,E123,E128,F401,F405,E402,F821,F841,E721,E265,F811,E302,E303,E741,E305,W504
17 | - cd examples
18 | - cd 2sectors && python start.py && cd ..
19 | #- cd 50000_firms && python start.py && cd ..
20 | - cd calendar && python start.py && cd ..
21 | #- cd cce && python start.py && cd ..
22 | #- cd create_and_delete_agents && python start.py && cd ..
23 | #- cd mesa_example && python start.py && cd ..
24 | #- cd monetary_shocks && python start.py && cd ..
25 | #- cd one_household_one_firm_with_logic && python start.py && cd ..
26 | # pid_controller
27 | # sugarscape
28 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "{}"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright {yyyy} {name of copyright owner}
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # abcEconomics examples
2 |
3 | To run the examples, first install the abcEconomics dependency
4 | ```
5 | pip install -r requirements.txt
6 | ```
7 | then run the start.py file from one of the examples.
8 | Note: the examples are not always working with the latest
9 | version of abcEconomics, so always check the version used
10 | in requirements.txt
11 |
12 | # Directory
13 |
14 | - [2sectors](examples/2sectors): The "hello, world!" of abcEconomics
15 | - [50000_firms](examples/50000_firms): Title says it all - 50k firms up and running
16 | - [calendar](examples/calendar): Shows how day/month can be incorporated
17 | - [create_and_delete_agents](examples/create_and_delete_agents): A simple illustration of how to create/delete agents during a
18 | simulation
19 | - [mesa_example](examples/mesa_example): A simple model of how to integrate abcEconomics and Mesa. The model and
20 | scheduler are implemented with abcEconomics while the space is implemented with Mesa.
21 | - [one_household_one_firm](examples/one_household_one_firm): TODO
22 | - [one_household_one_firm_with_logic](examples/one_household_one_firm_with_logic): one_household_one_firm with decision making
23 | control flow of a firm to choose an action (adjust_price)
24 | - [pid_controller](examples/pid_controller): TODO
25 | - [sugarscape](examples/sugarscape): Implementation of Sugarscape ({G1}, {M, T}) from Epstein
26 | Chapter 4. Heavily uses Mesa library.
27 |
--------------------------------------------------------------------------------
/examples/2sectors/firm.py:
--------------------------------------------------------------------------------
1 | import abcEconomics as abce
2 |
3 |
4 | class Firm(abce.Agent, abce.Firm):
5 | def init(self):
6 | """ there are now 2 sectors:
7 | - upstream produces an intermediary good
8 | - downstream uses labor and the intermediary good to produce the final good
9 |
10 | there is an initial endowment to avoid bootstrapping problems
11 | """
12 | self.price = {}
13 | if self.id % 2 == 0:
14 | assert self.id == 0
15 | self.create('money', 2)
16 | self.inputs = {"labor": 1}
17 | self.output = "intermediate_good"
18 | self.outquatity = 1
19 | self.price['intermediate_good'] = 1
20 | elif self.id % 2 == 1:
21 | assert self.id == 1
22 | self.create('money', 1)
23 | self.create('intermediate_good', 1)
24 | self.inputs = {"labor": 1, "intermediate_good": 1}
25 | self.output = "consumption_good"
26 | self.outquatity = 2
27 | self.price['consumption_good'] = 1
28 | self.pf = self.create_cobb_douglas(self.output, self.outquatity, self.inputs)
29 |
30 | def buy_inputs(self):
31 | oo = self.get_offers("labor")
32 | for offer in oo:
33 | self.accept(offer)
34 | oo = self.get_offers('intermediate_good')
35 | for offer in oo:
36 | self.accept(offer)
37 |
38 | def production(self):
39 | self.produce(self.pf, self.inputs)
40 |
41 | def sell_intermediary_goods(self):
42 | if self.output == 'intermediate_good':
43 | self.sell(('firm', 1), "intermediate_good", 1, 1)
44 | elif self.output == 'consumption_good':
45 | for i in range(2):
46 | self.sell(('household', i), 'consumption_good', 1, 1)
47 |
--------------------------------------------------------------------------------
/examples/2sectors/household.py:
--------------------------------------------------------------------------------
1 | import abcEconomics as abce
2 |
3 |
4 | class Household(abce.Agent, abce.Household):
5 | def init(self):
6 | """ self.employer is the _number_ of the agent that receives his
7 | labor offer.
8 | """
9 | self.labor_endowment = 1
10 | self.utility_function = self.create_cobb_douglas_utility_function({"consumption_good": 1})
11 | self.accumulated_utility = 0
12 | self.employer = self.id
13 | self._inventory._perishable.append('labor') # TODO simplify this
14 |
15 | def sell_labor(self):
16 | """ offers one unit of labor to firm self.employer, for the price of 1 "money" """
17 | self.sell(('firm', self.employer), "labor", quantity=1, price=1)
18 |
19 | def buy_intermediary_goods(self):
20 | """ recieves the offers and accepts them one by one """
21 | for offer in self.get_offers("consumption_good"):
22 | self.accept(offer)
23 |
24 | def consumption(self):
25 | """ consumes_everything and logs the aggregate utility. current_utiliy
26 | """
27 | current_utiliy = self.consume(self.utility_function, ['consumption_good'])
28 | self.accumulated_utility += current_utiliy
29 | self.log('HH', {'': self.accumulated_utility})
30 |
--------------------------------------------------------------------------------
/examples/2sectors/start.py:
--------------------------------------------------------------------------------
1 | """ Agents are now build according
2 | to the line in agents_parameter.csv
3 | """
4 | from abcEconomics import Simulation
5 | from firm import Firm
6 | from household import Household
7 |
8 |
9 | simulation_parameters = {'name': 'name',
10 | 'trade_logging': 'off',
11 | 'random_seed': None,
12 | 'rounds': 10}
13 |
14 | def main(simulation_parameters):
15 | w = Simulation()
16 |
17 | firms = w.build_agents(Firm, 'firm', 2)
18 | households = w.build_agents(Household, 'household', 2)
19 |
20 | for r in range(simulation_parameters['rounds']):
21 | w.advance_round(r)
22 | households.refresh_services('labor', derived_from='labor_endowment', units=5)
23 | # to access round, just get the value of w.round
24 | # to access its datetime version, use w._round # todo, better naming
25 | households.sell_labor()
26 | firms.buy_inputs()
27 | firms.production()
28 | firms.panel_log(goods=['consumption_good', 'intermediate_good'])
29 | firms.sell_intermediary_goods()
30 | households.buy_intermediary_goods()
31 | households.panel_log(goods=['consumption_good'])
32 | households.consumption()
33 | w.finalize()
34 |
35 |
36 | if __name__ == '__main__':
37 | main(simulation_parameters)
38 |
--------------------------------------------------------------------------------
/examples/50000_firms/b.py:
--------------------------------------------------------------------------------
1 | # Copyright 2012 Davoud Taghawi-Nejad
2 | #
3 | # Module Author: Davoud Taghawi-Nejad
4 | #
5 | # ABCE is open-source software. If you are using ABCE for your research you are
6 | # requested the quote the use of this software.
7 | #
8 | # Licensed under the Apache License, Version 2.0 (the "License"); you may not
9 | # use this file except in compliance with the License and quotation of the
10 | # author. You may obtain a copy of the License at
11 | # http://www.apache.org/licenses/LICENSE-2.0
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
15 | # License for the specific language governing permissions and limitations under
16 | # the License.
17 | # pylint: disable=W0212, C0111
18 | """ The best way to start creating a simulation is by copying the start.py file
19 | and other files from 'abce/template'.
20 |
21 | To see how to create a simulation read :doc:`Walk_through`. In this module you
22 | will find the explanation for the command.
23 |
24 | This is a minimal template for a start.py::
25 |
26 | from __future__ import division # makes / division work correct in python !
27 | from agent import Agent
28 | from abcEconomics import *
29 |
30 |
31 | simulation = Simulation(rounds=1000, name='sim')
32 | agents = simulation.build_agents(Agent, 'agent', 2)
33 | for round in simulation.next_round():
34 | agents.do('one')
35 | agents.do('two')
36 | agents.do('three')
37 | simulation.finalize()
38 | """
39 | from __future__ import division
40 | from __future__ import print_function
41 | from __future__ import absolute_import
42 | from builtins import zip
43 | from builtins import str
44 | from builtins import range
45 | from builtins import object
46 | import csv
47 | import datetime
48 | import os
49 | import time
50 | import multiprocessing as mp
51 | from multiprocessing.managers import BaseManager
52 | import abcEconomics.db as db
53 | import abcEconomics.abcelogger as abcelogger
54 | from . import postprocess
55 | from glob import glob
56 | from .firmmultitechnologies import *
57 | from .household import Household
58 | from .agent import *
59 | from .group import Group
60 | from collections import defaultdict, OrderedDict
61 | from .firm import Firm
62 | from .quote import Quote
63 | from .contracting import Contracting
64 | import json
65 | from . import abcEconomics as abcegui
66 | from .family import Family
67 | from .abcegui import gui
68 | import random
69 | from abce.notenoughgoods import NotEnoughGoods
70 |
71 |
72 | def execute_internal_wrapper(inp):
73 | return inp[0].execute_internal(inp[1])
74 |
75 |
76 | class MyManager(BaseManager):
77 | pass
78 |
79 |
80 | class Simulation(object):
81 | """ This class in which the simulation is run. Actions and agents have to be
82 | added. databases and resource declarations can be added. Then runs
83 | the simulation.
84 |
85 | Args:
86 | rounds:
87 | how many rounds the simulation lasts
88 |
89 | random_seed (optional):
90 | a random seed that controls the random number of the simulation
91 |
92 | trade_logging:
93 | Whether trades are logged,trade_logging can be
94 | 'group' (fast) or 'individual' (slow) or 'off'
95 |
96 | processes (optional):
97 | The number of processes that run in parallel. Each process hosts a share of
98 | the agents.
99 | Default is all your logical processor cores times two, using hyper-threading when available.
100 | For easy debugging set processes to one and the simulation is executed
101 | without parallelization.
102 | Sometimes it is advisable to decrease the number of processes to the number
103 | of logical or even physical processor cores on your computer.
104 | 'None' for all processor cores times 2.
105 | **For easy debugging set processes to 1, this way only one agent runs at
106 | a time and only one error message is displayed**
107 |
108 | Example::
109 |
110 | simulation = Simulation(rounds=1000, name='sim', trade_logging='individual', processes=None)
111 |
112 |
113 | Example for a simulation::
114 |
115 | num_firms = 5
116 | num_households = 200000
117 |
118 | w = Simulation(rounds=1000, name='sim', trade_logging='individual', processes=None)
119 |
120 | w.declare_round_endowment(resource='labor_endowment', productivity=1, product='labor')
121 | w.declare_round_endowment(resource='capital_endowment', productivity=1, product='capital')
122 |
123 | w.panel_data('firm', command='after_sales_before_consumption')
124 |
125 | firms = w.build_agents(Firm, 'firm', num_firms)
126 | households = w.build_agents(Household, 'household', num_households)
127 |
128 | all = firms + households
129 | for round in w.next_round():
130 | households.do('recieve_connections'),
131 | households.do('offer_capital'),
132 | firms.do('buy_capital'),
133 | firms.do('production'),
134 | if round = 250:
135 | centralbank.do('intervention)
136 | households.do(buy_product')
137 | all.do('after_sales_before_consumption')
138 | households.do('consume')
139 |
140 | w.finalize()
141 | """
142 |
143 | def __init__(self, rounds, name='abce', random_seed=None, trade_logging='off', processes=None):
144 | """
145 | """
146 | self.family_list = {}
147 | self.num_of_agents_in_group = {}
148 | self._messages = {}
149 | self._resource_command_group = {}
150 | self._db_commands = {}
151 | self.num_agents = 0
152 | self._build_first_run = True
153 | self.resource_endowment = defaultdict(list)
154 | self.perishable = []
155 | self.expiring = []
156 | self.variables_to_track_panel = defaultdict(list)
157 | self.variables_to_track_aggregate = defaultdict(list)
158 | self.possessins_to_track_panel = defaultdict(list)
159 | self.possessions_to_track_aggregate = defaultdict(list)
160 | self._start_round = 0
161 | self.round = int(self._start_round)
162 | self._calendar = False
163 | # this is default value as declared in self.network() method
164 | self._network_drawing_frequency = 1
165 |
166 | self.rounds = rounds
167 |
168 | try:
169 | os.makedirs(os.path.abspath('.') + '/result/')
170 | except OSError:
171 | pass
172 |
173 | self.path = (os.path.abspath('.') + '/result/' + name + '_' +
174 | datetime.datetime.now().strftime("%Y-%m-%d_%H-%M"))
175 | """ the path variable contains the path to the simulation outcomes it can be used
176 | to generate your own graphs as all resulting csv files are there.
177 | """
178 | while True:
179 | try:
180 | os.makedirs(self.path)
181 | break
182 | except OSError:
183 | self.path += 'I'
184 |
185 | self.trade_logging_mode = trade_logging
186 | if self.trade_logging_mode not in ['individual', 'group', 'off']:
187 | SystemExit("trade_logging can be "
188 | "'group' (fast) or 'individual' (slow) or 'off'"
189 | ">" + self.trade_logging_mode + "< not accepted")
190 |
191 | manager = mp.Manager()
192 | self.database_queue = manager.Queue()
193 | self._db = db.Database(
194 | self.path, self.database_queue, trade_log=self.trade_logging_mode != 'off')
195 | self.logger_queue = manager.Queue()
196 |
197 | self.processes = mp.cpu_count() * 2 if processes is None else processes
198 |
199 | MyManager.register('Family', Family)
200 | self.managers = []
201 | for i in range(self.processes):
202 | manager = MyManager()
203 | manager.start()
204 |
205 | self.managers.append(manager)
206 |
207 | if random_seed is None or random_seed == 0:
208 | random_seed = time.time()
209 | random.seed(random_seed)
210 |
211 | self.sim_parameters = OrderedDict(
212 | {'name': name, 'rounds': rounds, 'random_seed': random_seed})
213 |
214 | if self.processes > 1:
215 | self.pool = mp.Pool(self.processes)
216 | self.execute_internal = self.execute_internal_parallel if self.processes > 1 else self.execute_internal_seriel
217 | self._agents_to_add = [] # container used in self.run
218 | self._agents_to_delete = [] # container used in self.run
219 | self.messagess = defaultdict(list)
220 |
221 | def setup(self):
222 | manager = mp.Manager()
223 | all_families = [item for sublist in self.family_list.values()
224 | for item in sublist]
225 |
226 | for sender in all_families:
227 | for receiver in all_families:
228 | # same families in same manager can get the same manager.list()
229 | postbox = manager.list()
230 | for _ in receiver.iter_agents():
231 | # actually we need only sublists for agents actually in that family
232 | postbox.append([])
233 | sender.register_send_postbox(postbox)
234 | receiver.register_receive_postbox(postbox)
235 |
236 | def declare_round_endowment(self, resource, units, product, groups=['all']):
237 | """ At the beginning of very round the agent gets 'units' units of good 'product' for
238 | every 'resource' he possesses.
239 |
240 | Round endowments can be group specific, that means that when somebody except
241 | this group holds them they do not produce. The default is 'all'.
242 |
243 | Args::
244 |
245 | resource:
246 | The good that you have to hold to get the other
247 | units:
248 | the multiplier to get the produced good
249 | product:
250 | the good that is produced if you hold the first good
251 | groups:
252 | a list of agent groups, which gain the second good, if they hold the first one
253 |
254 | Example::
255 |
256 | A farmer gets a ton of harvest for every acre:
257 |
258 | w.declare_round_endowment(resource='land', units=1000, product='wheat')
259 |
260 |
261 | """
262 | if len(self.family_list) > 0:
263 | raise Exception(
264 | "WARNING: declare_round_endowment(...) must be called before the agents are build")
265 | for group in groups:
266 | self.resource_endowment[group].append((resource, units, product))
267 |
268 | def declare_perishable(self, good):
269 | """ This good only lasts one round and then disappears. For example
270 | labor, if the labor is not used today today's labor is lost.
271 | In combination with resource this is useful to model labor or capital.
272 |
273 | In the example below a worker has an endowment of labor and capital.
274 | Every round he can sell his labor service and rent his capital. If
275 | he does not the labor service for this round and the rent is lost.
276 |
277 | Args::
278 |
279 | good:
280 | the good that perishes
281 |
282 | Example::
283 |
284 | w.declare_perishable(good='LAB')
285 | w.declare_perishable(good='CAP')
286 |
287 | """
288 | if len(self.family_list) > 0:
289 | raise SystemExit(
290 | "WARNING: declare_perishable(...) must be called before the agents are build")
291 | self.perishable.append(good)
292 |
293 | def declare_expiring(self, good, duration):
294 | """ This type of good lasts for several rounds, but eventually
295 | expires. For example computers would last for several years and than
296 | become obsolete.
297 |
298 | Args:
299 |
300 | good:
301 | the good, which expires
302 | duration:
303 | the duration before the good expires
304 | """
305 | if len(self.family_list) > 0:
306 | raise SystemExit(
307 | "WARNING: declare_expiring(...) must be called before the agents are build")
308 | self.expiring.append((good, duration))
309 |
310 | def declare_service(self, human_or_other_resource, units, service, groups=['all']):
311 | """ When the agent holds the human_or_other_resource, he gets 'units' of service every round
312 | the service can be used only with in this round.
313 |
314 | Args::
315 |
316 | human_or_other_resource:
317 | the good that needs to be in possessions to create the other good 'self.create('adult', 2)'
318 | units:
319 | how many units of the service is available
320 | service:
321 | the service that is created
322 | groups:
323 | a list of agent groups that can create the service
324 |
325 | Example::
326 |
327 | For example if a household has two adult family members, it gets 16 hours of work
328 |
329 | w.declare_service('adult', 8, 'work')
330 | """
331 | self.declare_round_endowment(
332 | human_or_other_resource, units, service, groups)
333 | self.declare_perishable(service)
334 |
335 | def declare_calendar(self, year=None, month=1, day=1):
336 | """ ABCE can run in two time model:
337 |
338 | rounds:
339 | As default ABCE runs in round mode. Agents can access the self.round
340 | variable to find out what round it is. The execution condition in the
341 | action_list is based on rounds.
342 |
343 | calendar:
344 | Every iteration over the action_list is a day. Agents can access
345 | self.date() to find out what date it is.
346 |
347 | Args:
348 | year, month, day
349 |
350 | Example::
351 |
352 | simulation.declare_calendar(2000, 1, 1)
353 | simulation.declare_calendar() # starts at current date
354 |
355 | """
356 | if len(self.family_list) > 0:
357 | raise SystemExit(
358 | "WARNING: declare_calendar(...) must be called before the agents are build")
359 | date = datetime.date.today() if year is None else datetime.date(year, month, day)
360 |
361 | self._start_round = date.toordinal()
362 | self.round = int(self._start_round)
363 | self._calendar = True
364 |
365 | def panel(self, group, possessions=[], variables=[]):
366 | """ panel(.) writes a panel of variables and possessions
367 | of a group of agents into the database, so that it is displayed
368 | in the gui. Aggregate must be declared before the agents are build.
369 | ('agent_group', 'panel') must be in the action_list, so that
370 | the simulation knows when to make the aggregate snapshot.
371 |
372 | Args:
373 | group:
374 | can be either a group or 'all' for all agents
375 | possessions (list, optional):
376 | a list of all possessions you want to track as 'strings'
377 | variables (list, optional):
378 | a list of all variables you want to track as 'strings'
379 |
380 | Example in start.py::
381 |
382 | simulation_parameters.build_agents(Firm, 'firm', number=5)
383 |
384 | ...
385 |
386 | simulation.panel('firm', possessions=['money', 'input'],
387 | variables=['production_target', 'gross_revenue'])
388 |
389 | for round in simulation.next_round():
390 | firms.do('produce_and_sell)
391 | firms.do('panel')
392 | households.do('buying')
393 | """
394 | if len(self.family_list) > 0:
395 | raise SystemExit(
396 | "WARNING: panel(...) must be called before the agents are build")
397 | self._db.add_panel(group)
398 | self.variables_to_track_panel[group] = variables
399 | self.possessins_to_track_panel[group] = possessions
400 |
401 | def aggregate(self, group, possessions=[], variables=[]):
402 | """ aggregate(.) writes summary statistics of variables and possessions
403 | of a group of agents into the database, so that it is displayed in
404 | the gui. Aggregate must be declared before the agents are build.
405 | ('agent_group', 'aggregate') must be in the action_list, so that
406 | the simulation knows when to make the aggregate snapshot.
407 |
408 |
409 | Args:
410 | group:
411 | can be either a group or 'all' for all agents
412 | possessions (list, optional):
413 | a list of all possessions you want to track as 'strings'
414 | variables (list, optional):
415 | a list of all variables you want to track as 'strings'
416 |
417 | Example in start.py::
418 |
419 |
420 | simulation_parameters.build_agents(Firm, 'firm', number=5)
421 |
422 | ...
423 |
424 | simulation.aggregate('firm', possessions=['money', 'input'],
425 | variables=['production_target', 'gross_revenue'])
426 |
427 | for round in simulation.next_round():
428 | firms.do('produce_and_sell)
429 | firms.do('aggregate')
430 | households.do('buying')
431 |
432 |
433 |
434 | """
435 | if len(self.family_list) > 0:
436 | raise SystemExit(
437 | "WARNING: aggregate(...) must be called before the agents are build")
438 | self._db.add_aggregate(group)
439 | self.variables_to_track_aggregate[group] = variables
440 | self.possessions_to_track_aggregate[group] = possessions
441 |
442 | def network(self, frequency=1, savefig=False, savegml=True,
443 | figsize=(24, 20), dpi=100, pos_fixed=False, alpha=0.8):
444 | """ network(.) prepares abce to write network data.
445 |
446 | Args:
447 | frequency:
448 | the frequency with which the network is written, default=1
449 | savefig:
450 | wether to save a png file, default=False
451 | savegml:
452 | wether to save a gml file, default=True
453 | figsize:
454 | size of the graph in inch. (see matplotlib)
455 | dpi:
456 | resulution of the picture
457 | pos_fixed:
458 | positions are fixed after the first round
459 |
460 | Example::
461 |
462 | simulation.network(savefig=True)
463 | """
464 | self._network_drawing_frequency = frequency
465 | self._logger = abcelogger.AbceLogger(
466 | self.path,
467 | self.logger_queue,
468 | savefig=savefig,
469 | savegml=savegml,
470 | figsize=figsize,
471 | dpi=dpi,
472 | pos_fixed=pos_fixed,
473 | alpha=alpha)
474 | self._logger.start()
475 |
476 | def execute_internal_seriel(self, command):
477 | for group in self.family_list:
478 | for family in self.family_list[group]:
479 | family.execute_internal(command)
480 |
481 | def execute_internal_parallel(self, command):
482 | parameters = ((family, command)
483 | for group in self.family_list for family in self.family_list[group])
484 | families_messages = self.pool.map(
485 | execute_internal_wrapper, parameters, chunksize=1)
486 |
487 | def _prepare(self):
488 | """ This runs the simulation """
489 | if not(self.family_list):
490 | raise SystemExit('No Agents Created')
491 |
492 | self._db.start()
493 |
494 | self._write_description_file()
495 | self._displaydescribtion()
496 |
497 | self.clock = time.time()
498 |
499 | def next_round(self):
500 | if self.round - self._start_round == 0: # the simulation has just started
501 | self._prepare()
502 | while self.rounds > self.round - self._start_round:
503 | if self.round > self._start_round:
504 | self._finalize_prev_round(self.round - 1)
505 | self._prepare_round(self.round)
506 | yield self.round
507 | self.round += 1
508 | if self.round - self._start_round == self.rounds:
509 | self._finalize()
510 |
511 | def _prepare_round(self, round):
512 | self._round = datetime.date.fromordinal(
513 | round) if self._calendar else round
514 | print("\rRound" + str(" %3d " % round) + str(self._round))
515 | self.execute_internal('_produce_resource')
516 |
517 | def _finalize_prev_round(self, round):
518 | self.execute_internal('_advance_round')
519 | self.add_agents(round)
520 | self.delete_agent()
521 |
522 | def gracefull_exit(self):
523 | self.database_queue.put('close')
524 | self.logger_queue.put(['close', 'close', 'close'])
525 |
526 | try:
527 | while self._logger.is_alive():
528 | time.sleep(0.05)
529 | except AttributeError:
530 | pass
531 |
532 | while self._db.is_alive():
533 | time.sleep(0.05)
534 |
535 | try:
536 | self.pool.close()
537 | self.pool.join()
538 | except AttributeError:
539 | pass
540 |
541 | def _finalize(self):
542 | print(str("time only simulation %6.2f" % (time.time() - self.clock)))
543 | self.gracefull_exit()
544 | print(str("time with data and network %6.2f" %
545 | (time.time() - self.clock)))
546 | if self.round > 0:
547 | postprocess.to_csv(os.path.abspath(self.path), self._calendar)
548 | print(str("time with post processing %6.2f" %
549 | (time.time() - self.clock)))
550 |
551 | def build_agents(self, AgentClass, group_name, number=None, parameters={}, agent_parameters=None):
552 | """ This method creates agents.
553 |
554 | Args:
555 |
556 | AgentClass:
557 | is the name of the AgentClass that you imported
558 |
559 | group_name:
560 | the name of the group, as it will be used in the action list and transactions.
561 | Should generally be lowercase of the AgentClass.
562 |
563 | number:
564 | number of agents to be created.
565 |
566 | group_name (optional):
567 | to give the group a different name than the lowercase
568 | class_name.
569 |
570 | parameters:
571 | a dictionary of parameters
572 |
573 | agent_parameters:
574 | a list of dictionaries, where each agent gets one dictionary.
575 | The number of agents is the length of the list
576 |
577 | Example::
578 |
579 | firms = simulation.build_agents(Firm, 'firm', number=simulation_parameters['num_firms'])
580 | banks = simulation.build_agents(Bank, 'bank', parameters=simulation_parameters, agent_parameters=[{'name': UBS'},{'name': 'amex'},{'name': 'chase'})
581 | centralbanks = simulation.build_agents(CentralBank, 'centralbank', number=1, parameters={'rounds': num_rounds})
582 | """
583 | assert number is None or agent_parameters is None, 'either set number or agent_parameters in build_agents'
584 | if number is not None:
585 | num_agents_this_group = number
586 | agent_parameters = [None] * num_agents_this_group
587 | else:
588 | num_agents_this_group = len(agent_parameters)
589 |
590 | self.family_list[group_name] = []
591 |
592 | self.sim_parameters.update(parameters)
593 |
594 | agent_params_from_sim = {'expiring': self.expiring,
595 | 'perishable': self.perishable,
596 | 'resource_endowment': self.resource_endowment[group_name] + self.resource_endowment['all'],
597 | 'panel': (self.possessins_to_track_panel[group_name],
598 | self.variables_to_track_panel[group_name]),
599 | 'aggregate': (self.possessions_to_track_aggregate[group_name],
600 | self.variables_to_track_aggregate[group_name]),
601 | 'ndf': self._network_drawing_frequency}
602 |
603 | for i, manager in enumerate(self.managers):
604 | family = manager.Family(AgentClass,
605 | num_agents_this_group=num_agents_this_group,
606 | batch=i,
607 | num_managers=self.processes,
608 | agent_args={'group': group_name,
609 | 'trade_logging': self.trade_logging_mode,
610 | 'database': self.database_queue,
611 | 'logger': self.logger_queue,
612 | 'random_seed': random.random(),
613 | 'start_round': self._start_round},
614 | parameters=parameters,
615 | agent_parameters=agent_parameters,
616 | agent_params_from_sim=agent_params_from_sim)
617 | self.family_list[group_name].append(family)
618 | self.num_of_agents_in_group[group_name] = num_agents_this_group
619 | return Group(self, [self.family_list[group_name]], group_name)
620 |
621 | def add_agents(self, round):
622 | messages = self._agents_to_add
623 | if len(messages) == 0:
624 | return
625 | for _, _, (AgentClass, group_name, parameters, agent_parameters) in messages:
626 | id = self.num_of_agents_in_group[group_name]
627 | self.num_of_agents_in_group[group_name] += 1
628 | assert len(
629 | self.family_list[group_name]) == self.processes, "the expandable parameter in build_agents must be set to true"
630 | family = self.family_list[group_name][id % self.processes]
631 |
632 | family.append(AgentClass, id=id,
633 | agent_args={'group': group_name,
634 | 'trade_logging': self.trade_logging_mode,
635 | 'database': self.database_queue,
636 | 'logger': self.logger_queue,
637 | 'random_seed': random.random(),
638 | 'start_round': round + 1},
639 | parameters=parameters, agent_parameters=agent_parameters)
640 | self._agents_to_add = []
641 |
642 | def delete_agent(self):
643 | messages = self._agents_to_delete
644 | if len(messages) == 0:
645 | return
646 | dest_family = defaultdict(list)
647 | for _, _, (group_name, id, quite) in messages:
648 | dest_family[(group_name, id % self.processes, quite)].append(id)
649 |
650 | for (group_name, family_id, quite), ids in dest_family.items():
651 | family = self.family_list[group_name][family_id]
652 | if quite:
653 | family.replace_with_dead(ids)
654 | else:
655 | family.remove(ids)
656 | self._agents_to_delete = []
657 |
658 | def _write_description_file(self):
659 | description = open(os.path.abspath(
660 | self.path + '/description.txt'), 'w')
661 | description.write(json.dumps(self.sim_parameters, indent=4,
662 | skipkeys=True, default=lambda x: 'not_serializeable'))
663 |
664 | def _displaydescribtion(self):
665 | description = open(self.path + '/description.txt', 'r')
666 | print(description.read())
667 |
668 | def graphs(self, open=True, new=1):
669 | """ after the simulatio is run, graphs() shows graphs of all data
670 | collected in the simulation. Shows the same output as the @gui
671 | decorator shows.
672 |
673 | Args:
674 |
675 | open (True/False):
676 | whether to open a new window
677 |
678 | new:
679 | If new is 0, the url is opened in the same browser window if
680 | possible. If new is 1, a new browser window is opened if
681 | possible. If new is 2, a new browser page (tab) is opened
682 | if possible.
683 |
684 | Example::
685 |
686 | simulation = Simulation(...)
687 | for round in simulation.next_round():
688 | ...
689 |
690 | simulation.graphs()
691 | """
692 | if self.round > 0:
693 | abcegui.run(open=open, new=new)
694 |
695 | def pickle(self, name):
696 | with open('%s.simulation' % name, 'wb') as jar:
697 | json.dump({'year': self.rounds,
698 | 'agents': [agent.__dict__ for agent in self.num_of_agents_in_group['all']],
699 | 'messages': self.messagess},
700 | jar, default=handle_non_pickleable)
701 |
702 | def unpickle(self, name):
703 | with open('%s.simulation' % name, 'rb') as jar:
704 | simulation = json.load(jar)
705 |
706 | self._start_round = simulation['year']
707 |
708 | all_agents_values = simulation['agents']
709 | for agent, agent_values in zip(self.num_of_agents_in_group['all'], all_agents_values):
710 | for key, value in agent_values.items():
711 | if value != "NotPickleable":
712 | if key not in agent.__dict__:
713 | agent.__dict__[key] = value
714 | elif isinstance(agent.__dict__[key], defaultdict):
715 | try:
716 | agent.__dict__[key] = defaultdict(
717 | type(list(value.values())[0]), value)
718 | except IndexError:
719 | agent.__dict__[key] = defaultdict(float)
720 | elif isinstance(agent.__dict__[key], OrderedDict):
721 | agent.__dict__[key] = OrderedDict(value)
722 | elif isinstance(agent.__dict__[key], np.ndarray):
723 | agent.__dict__[key] = np.array(value)
724 | else:
725 | agent.__dict__[key] = value
726 |
727 | for agent in self.num_of_agents_in_group['all']:
728 | self.num_of_agents_in_group[agent.group][agent.id] = agent
729 |
730 | self._messages = simulation['messages']
731 |
732 |
733 | def handle_non_pickleable(x):
734 | if isinstance(x, np.ndarray):
735 | return list(x)
736 | else:
737 | return "NotPickleable"
738 |
739 |
740 | def _number_or_string(word):
741 | """ returns a int if possible otherwise a float from a string
742 | """
743 | try:
744 | return int(word)
745 | except ValueError:
746 | try:
747 | return float(word)
748 | except ValueError:
749 | return word
750 |
--------------------------------------------------------------------------------
/examples/50000_firms/myagent.py:
--------------------------------------------------------------------------------
1 | from abcEconomics.agent import Agent
2 | from random import shuffle
3 |
4 |
5 | class MyAgent(Agent):
6 | def init(self):
7 | # print("m", self.id)
8 | pass
9 |
10 | def compute(self):
11 | # print('here', self.id)
12 | lst = list(range(1))
13 | shuffle(lst)
14 | max(lst)
15 |
16 | def g(self):
17 | for offer in self.get_offers('cookie'):
18 | self.accept(offer)
19 | # print(self.possession('cookie'))
20 |
--------------------------------------------------------------------------------
/examples/50000_firms/start.py:
--------------------------------------------------------------------------------
1 | from myagent import MyAgent
2 | from youragent import YourAgent
3 | from abcEconomics import Simulation
4 |
5 |
6 | def main():
7 | s = Simulation(processes=1)
8 |
9 | myagents = s.build_agents(MyAgent, 'myagent', 50000)
10 | youragents = s.build_agents(YourAgent, 'youragent', 50000)
11 |
12 | for r in range(100):
13 | print('Round: %i' % r)
14 | s.advance_round(r)
15 | # (myagents+youragents).do('compute')
16 | youragents.s()
17 | myagents.g()
18 | s.finalize()
19 |
20 |
21 | if __name__ == '__main__':
22 | main()
23 |
--------------------------------------------------------------------------------
/examples/50000_firms/start.py.lprof:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AB-CE/examples/144e40de3f7f4a9e36b12af9e1b8a6ed613fee99/examples/50000_firms/start.py.lprof
--------------------------------------------------------------------------------
/examples/50000_firms/youragent.py:
--------------------------------------------------------------------------------
1 | from abcEconomics.agent import Agent
2 | from random import shuffle, randint
3 |
4 |
5 | class YourAgent(Agent):
6 | def init(self):
7 | pass
8 |
9 | def compute(self):
10 | lst = list(range(1))
11 | shuffle(lst)
12 | max(lst)
13 |
14 | def s(self):
15 | self.create('cookie', 1)
16 | self.sell(('myagent', randint(0, self.id)),
17 | good='cookie', price=0, quantity=1)
18 | assert self['cookie'] == 1
19 |
--------------------------------------------------------------------------------
/examples/calendar/agent.py:
--------------------------------------------------------------------------------
1 | import abcEconomics as abce
2 |
3 |
4 | class Agent(abce.Agent):
5 | def init(self):
6 | # your agent initialization goes here, not in __init__
7 | pass
8 |
9 | def wednessday(self):
10 | print('wednessday')
11 |
12 | def first(self):
13 | print('first')
14 | self.create('money', 1000)
15 |
16 | def newyearseve(self):
17 | print('newyearseve')
18 |
19 | def firstfriday(self):
20 | print('drinks in museum')
21 |
22 | def fiveteens(self):
23 | print('fiveteens')
24 |
25 | def everythreedays(self):
26 | print((' ', self.date(), self.date().weekday()))
27 |
--------------------------------------------------------------------------------
/examples/calendar/start.py:
--------------------------------------------------------------------------------
1 | from agent import Agent
2 | from abcEconomics import Simulation
3 |
4 | def main():
5 | """ Demonstration of the simulation.advance_round function, which can
6 | take arbitrary values """
7 | simulation = Simulation()
8 | agents = simulation.build_agents(Agent, 'agent', number=1)
9 |
10 | weekday = 0
11 | for year in range(2000, 2010):
12 | for month in range(12):
13 | for day in range(30):
14 | simulation.advance_round((year, month, day))
15 | weekday = (weekday + 1) % 7
16 | print(weekday)
17 | if weekday == 3:
18 | agents.wednessday()
19 | if day == 1:
20 | agents.first()
21 | if month == 12 and day == 31:
22 | agents.newyearseve()
23 | if day <= 7 and weekday == 5:
24 | agents.firstfriday()
25 | if day == 15:
26 | agents.fiveteens()
27 | agents.panel_log(goods=['money'])
28 | agents.agg_log(goods=['labor'])
29 | simulation.finalize()
30 |
31 |
32 | if __name__ == '__main__':
33 | main()
34 |
--------------------------------------------------------------------------------
/examples/cce/climate_square.sam.csv:
--------------------------------------------------------------------------------
1 | index,col,ele,gas,o_g,oil,eis,trn,roe,lab,cap,hoh,inv,nx,tax,sum
col,0.243,1.448,0.004,0,0.001,0.219,0.013,0.238,,,0.014,0,0.108,,2.288
ele,0.052,0.084,0.027,0.118,0.168,1.384,0.283,9.53,,,12.915,0,-0.093,,24.468
gas,0.003,0.526,2.283,0.446,0.246,0.817,0.056,2.199,,,4.136,0,0.045,,10.757
o_g,0,0.024,4.795,2.675,8.381,0.939,0.03,0.12,,,0.013,0.072,-6.189,,10.86
oil,0.066,0.238,0.038,0.072,1.753,0.628,2.428,4.95,,,8.345,0.128,-0.542,,18.104
eis,0.101,0.121,0.015,0.285,0.513,17.434,0.177,47.534,,,9.239,0.906,-3.506,,72.819
trn,0.158,0.945,0.135,0.122,0.784,3.548,9.796,19.835,,,17.316,1.492,5.107,,59.238
roe,0.747,5.142,1.897,4.694,2.798,19.974,16.055,540.977,,,751.254,203.063,-21.406,,1525.195
lab,0.437,4.422,0.434,0.665,1.141,16.128,19.032,553.948,,,,,,,596.207
cap,0.278,8.83,0.866,1.525,2.115,10.806,9.792,310.641,,,,,,,344.853
hoh,,,,,,,,,596.207,344.853,,,26.476,41.357,1008.893
inv,,,,,,,,,,,205.661,,,,205.661
nx,,,,,,,,,,,,,,,0
tax,0.203,2.686,0.263,0.258,0.204,0.944,1.574,35.225,,,,,,,41.357
sum,2.288,24.466,10.757,10.86,18.104,72.821,59.236,1525.197,596.207,344.853,1008.893,205.661,0,41.357,
--------------------------------------------------------------------------------
/examples/cce/coal_produced_comment.txt:
--------------------------------------------------------------------------------
1 | col produced comment
2 | Often immediately after the introduction
3 | of a new tax regime the economy reacts
4 | very strongly or exhibits fluctuations.
5 | This is a model limitation. In reality,
6 | firms and consumers have foresight, which
7 | gives them the ability to smooth their
8 | production and consumption decisions.
9 | The model agents don't have this ability
10 | and therefore we see strong fluctuations.
--------------------------------------------------------------------------------
/examples/cce/firm.py:
--------------------------------------------------------------------------------
1 | #pylint: disable=W0201
2 | import random
3 | from copy import copy
4 | from collections import OrderedDict
5 | import itertools
6 |
7 | import numpy as np
8 | import abcEconomics as abce
9 | from optimization_functions import optimization
10 |
11 | def normalized_random(length):
12 | random_values = [random.uniform(0.1, 0.9) for _ in range(length)]
13 | sum_values = sum(random_values)
14 | return np.array([v / sum_values for v in random_values])
15 |
16 | class GoodDetails:
17 | def __init__(self, betas, capital_types, num_firms):
18 | self.entities = OrderedDict()
19 | self.ids = OrderedDict()
20 | self.goods = OrderedDict()
21 | self.prices = OrderedDict()
22 | self.weights = OrderedDict()
23 | self.betas = []
24 | for good, value in betas.items():
25 | if value > 0:
26 | self.betas.append(value)
27 | if good in capital_types:
28 | self.entities[good] = ['household']
29 | self.ids[good] = [0]
30 | self.goods[good] = [good]
31 | self.prices[good] = [None]
32 | self.weights[good] = [None]
33 | else:
34 | self.entities[good] = [good for id in range(num_firms)]
35 | self.ids[good] = [id for id in range(num_firms)]
36 | self.goods[good] = [good for id in range(num_firms)]
37 | self.prices[good] = [None for id in range(num_firms)]
38 | self.weights[good] = [None for id in range(num_firms)]
39 |
40 | def list_of_cheapest_offers(self):
41 | cheapest_offers = []
42 | for good in self.goods:
43 | cheapest_offers.append(min(self.prices[good]))
44 | return np.array(cheapest_offers, dtype=float)
45 |
46 | def update_weights_optimal_from_partial_list(self, weights):
47 | for good in self.goods:
48 | for i in range(len(self.weights[good])):
49 | self.weights[good][i] = 0
50 |
51 | for i, good in enumerate(self.goods):
52 | index = np.argmax(self.prices[good])
53 | self.weights[good][index] = weights[i]
54 |
55 | def weights_as_list(self):
56 | weights = [w for w in self.weights.values()]
57 | return np.array(list(itertools.chain.from_iterable(weights)))
58 |
59 | def set_weights_from_full_list(self, weights):
60 | i = 0
61 | for sublist in self.weights.values():
62 | for s in range(len(sublist)):
63 | sublist[s] = weights[i]
64 | i += 1
65 |
66 | def set_prices_from_list(self, prices):
67 | i = 0
68 | for sublist in self.prices.values():
69 | for s in range(len(sublist)):
70 | sublist[s] = prices[i]
71 | i += 1
72 |
73 | def set_price(self, good, nr, price):
74 | self.prices[good][nr] = price
75 |
76 | def __len__(self):
77 | return sum([len(entry) for entry in self.entities.values()])
78 |
79 | def num_goods(self):
80 | return len(self.entities)
81 |
82 | def __iter__(self):
83 | for good in self.entities:
84 | for x in zip(self.entities[good], self.ids[good], self.goods[good], self.prices[good], self.weights[good]):
85 | yield x
86 |
87 |
88 | class Firm(abce.Agent, abce.Firm):
89 | def init(self, num_firms, price_stickiness, network_weight_stickiness, dividends_percent, capital_types,
90 | output_tax_shares, production_functions, outputs, money, sam, tax_change_time, carbon_prod, carbon_tax,
91 | tax, **trash):
92 | self.num_firms = num_firms
93 | self.price_stickiness = price_stickiness
94 | self.dividends_percent = dividends_percent
95 | self.network_weight_stickiness = network_weight_stickiness
96 | self.capital_types = capital_types
97 | self.output_tax_share = output_tax_shares[self.group]
98 | production_function = production_functions[self.group]
99 | money = money / 2 / (self.num_firms * len(outputs))
100 | betas = production_function[1]
101 | self.sbtax = sam.entries['tax'][self.group]
102 | self.value_of_international_sales = sam.endowment_vector('nx')[self.group]
103 | self.value_of_investment = sam.endowment_vector('inv')[self.group]
104 | self.tax_change_time = tax_change_time
105 | self.carbon_prod = carbon_prod[self.group] / (sam.column_sum[self.group] - sam.entries[self.group]['nx'])
106 | self.carbon_tax_after = carbon_tax * 12 / 44
107 | self.carbon_tax = 0
108 |
109 | self.after_policy_change_output_tax_share = tax[self.group] / 100
110 |
111 | self.goods_details = GoodDetails(betas, self.capital_types, self.num_firms)
112 | self.goods_details.set_prices_from_list(normalized_random(len(self.goods_details)))
113 |
114 | self.seed_weights = normalized_random(self.goods_details.num_goods())
115 | self.goods_details.set_weights_from_full_list(normalized_random(len(self.goods_details)))
116 |
117 | self.create(self.group, sam.column_sum[self.group])
118 | # initial endowment of own good and price must be consistent (=the same)
119 | self.create('money', money)
120 | self.money_1 = self.possession('money')
121 |
122 | self.price = 1
123 | self.profit = 0
124 |
125 | self.b = production_function[0]
126 | self.beta = {good: value for good, value in production_function[1].items() if value > 0}
127 |
128 | self.set_cobb_douglas(self.group, self.b, self.beta)
129 | self.sales = []
130 | self.nx = 0
131 |
132 | def taxes_intervention(self):
133 | if self.round == self.tax_change_time:
134 | self.carbon_tax = self.carbon_tax_after
135 | self.output_tax_share = self.after_policy_change_output_tax_share
136 |
137 | def international_trade(self):
138 | if self.value_of_international_sales > 0:
139 | value = min(self.value_of_international_sales, self.possession(self.group))
140 | sale = self.sell(('netexport', 0), good=self.group, quantity=value, price=self.price)
141 | self.sales.append(sale)
142 | else:
143 | value = min(- self.value_of_international_sales, self.possession('money') / self.price)
144 | self.buy(('netexport', 0), good=self.group, quantity=value, price=self.price)
145 | self.nx = value
146 |
147 | def invest(self):
148 | if self.value_of_investment > 0:
149 | value = min(self.value_of_investment, self.possession(self.group))
150 | sale = self.sell(('netexport', 0), good=self.group, quantity=value, price=self.price)
151 | self.sales.append(sale)
152 |
153 | def send_demand(self):
154 | """ send nominal demand, according to weights to neighbor """
155 | for entity, id, good, _, weight in self.goods_details:
156 | self.send((entity, id),
157 | good,
158 | weight * self.possession('money'))
159 |
160 | def selling(self):
161 | """ receive demand from neighbors and consumer;
162 | calculate market_clearing_price, adaped the price slowly
163 | and sell the good to the neighbors, the quantity might
164 | be rationed.
165 | """
166 | messages = self.get_messages(self.group)
167 | nominal_demand = [msg.content for msg in messages]
168 | self.nominal_demand = sum(nominal_demand)
169 | if self.possession(self.group) > 0:
170 | market_clearing_price = (sum(nominal_demand) / self.possession(self.group))
171 | self.price = (1 - self.price_stickiness) * market_clearing_price + self.price_stickiness * self.price
172 | demand = sum([msg.content / self.price for msg in messages])
173 | if demand < self.possession(self.group):
174 | self.rationing = rationing = 1
175 | else:
176 | self.rationing = rationing = max(0, self.possession(self.group) / demand)
177 |
178 | for msg in messages:
179 | quantity = msg.content / self.price * rationing
180 | assert not np.isnan(quantity), (msg.content, self.price, rationing)
181 | sale = self.sell(msg.sender, good=self.group, quantity=quantity, price=self.price)
182 | self.sales.append(sale)
183 | else:
184 | for msg in messages:
185 | sale = self.sell((msg.sender_group, msg.sender_id), good=self.group, quantity=0, price=self.price)
186 | self.sales.append(sale)
187 |
188 | def sales_tax(self):
189 | total_sales_quantity = sum([sale.final_quantity for sale in self.sales]) - self.nx
190 | tax = (total_sales_quantity * self.price) * self.output_tax_share
191 | self.give(('government', 0), good='money', quantity=min(self.possession('money'), tax))
192 | self.sales = []
193 |
194 | def carbon_taxes(self):
195 | carbon_tax = self.produced * self.carbon_prod * self.carbon_tax * (1 - self.output_tax_share)
196 | self.give(('government', 0), good='money', quantity=min(self.possession('money'), carbon_tax))
197 |
198 | def buying(self):
199 | """ get offers from each neighbor, accept it and update
200 | neighbor_prices and neighbors_goods """
201 | for offers in self.get_offers_all().values():
202 | for offer in offers:
203 | self.accept(offer)
204 | self.goods_details.set_price(offer.good, offer.sender_id, offer.price)
205 |
206 | def production(self):
207 | """ produce using all goods and labor """
208 | input_goods = {input: self.possession(input) for input in self.beta.keys()}
209 | self.input_goods = copy(input_goods)
210 | p = self.produce(input_goods)
211 | self.produced = p[self.group]
212 |
213 | def dividends(self):
214 | """ pay dividends to household if profit is positive, calculate profits """
215 | self.profit = self.possession('money') - self.money_1
216 | earnings = max(0, self.profit)
217 | self.give(('household', 0), good='money', quantity=self.dividends_percent * earnings)
218 | self.money_1 = self.possession('money')
219 |
220 | def change_weights(self):
221 | opt = optimization(seed_weights=self.seed_weights,
222 | input_prices=self.goods_details.list_of_cheapest_offers(),
223 | b=self.b,
224 | beta=self.goods_details.betas,
225 | method='SLSQP')
226 | if not opt.success:
227 | print(self.round, self.name, opt.message)
228 | print(zip(self.goods_details.goods.keys(), self.goods_details.list_of_cheapest_offers().tolist()))
229 | raise Exception('Optimization error')
230 |
231 | self.seed_weights = opt.x
232 |
233 | old_weighs = self.goods_details.weights_as_list()
234 | self.goods_details.update_weights_optimal_from_partial_list(opt.x)
235 | optimal_weights = self.goods_details.weights_as_list()
236 |
237 | weights = (self.network_weight_stickiness * old_weighs +
238 | (1 - self.network_weight_stickiness) * optimal_weights)
239 |
240 | weights = weights / sum(weights)
241 |
242 | self.goods_details.set_weights_from_full_list(weights)
243 |
244 | def stats(self):
245 | """ helper for statistics """
246 | self.co2 = self.produced * self.carbon_prod
247 |
--------------------------------------------------------------------------------
/examples/cce/government.py:
--------------------------------------------------------------------------------
1 | #pylint: disable=W0201
2 | import abcEconomics as abce
3 |
4 |
5 | class Government(abce.Agent):
6 | def init(self, simulation_parameters, _):
7 | self.num_households = simulation_parameters['num_household']
8 |
9 | def taxes_to_household(self):
10 | self.money = self.possession('money')
11 | share = self.money / self.num_households
12 | for i in range(self.num_households):
13 | self.give(('household', i), good='money', quantity=share)
14 |
--------------------------------------------------------------------------------
/examples/cce/hirachical_taxes_nx.sam.csv:
--------------------------------------------------------------------------------
1 | index,brd,mlk,tools,cap,lab,tax,hoh,nx,sum
2 | brd,,,,,,,24,-2,22
3 | mlk,,,,,,,43.1,2,45.1
4 | tools,5,6,,,,,,,11
5 | cap,5,20,5,,,,,,30
6 | lab,10,15,5,,,,,,30
7 | tax,2,4.1,1,,,,,,7.1
8 | hoh,,,,30,30,7.1,,,67.1
9 | sum,22,45.1,11,30,30,7.1,67.1,,
10 | ,X,X,X,X,X,X,X,,
--------------------------------------------------------------------------------
/examples/cce/household.py:
--------------------------------------------------------------------------------
1 | import abcEconomics as abce
2 | from abcEconomics import NotEnoughGoods
3 | from pprint import pprint
4 | from sys import float_info
5 | from collections import defaultdict
6 |
7 | class Household(abce.Agent, abce.Household):
8 | def init(self, simulation_parameters, _):
9 | self.num_firms = num_firms = simulation_parameters['num_firms']
10 | self.wage_stickiness = simulation_parameters['wage_stickiness']
11 | money = simulation_parameters['money'] / 2
12 |
13 | self.create('money', money)
14 | self.utility = 0
15 |
16 | self.final_goods = simulation_parameters['final_goods']
17 | self.alpha = simulation_parameters['consumption_functions']['hoh']
18 | self.create('endowment_FFcap', simulation_parameters['endowment_FFcap'])
19 | self.create('endowment_FFlab', simulation_parameters['endowment_FFlab'])
20 |
21 | self.set_cobb_douglas_utility_function(self.alpha)
22 | self.sells = []
23 | self.welfare = 0
24 |
25 | def send_demand(self):
26 | for final_good in self.final_goods:
27 | for i in range(self.num_firms):
28 | demand = self.alpha[final_good] / self.num_firms * self.possession("money")
29 | if demand > 0:
30 | self.send((final_good, i), final_good, demand)
31 |
32 | def selling(self):
33 | """ receive demand from neighbors and consumer;
34 | calculate market_clearing_price, adapted the price slowly
35 | and sell the good to the neighbors, the quantity might
36 | be rationed.
37 | """
38 | messages = self.get_messages_all()
39 | for capital_type, ct_messages in messages.items():
40 | nominal_demand = [msg.content for msg in ct_messages]
41 | market_clearing_price = sum(nominal_demand) / self.possession(capital_type)
42 | if self.round > 5:
43 | self.price = price = (1 - self.wage_stickiness) * market_clearing_price + self.wage_stickiness * self.price
44 | else:
45 | self.price = price = market_clearing_price
46 | demand = sum([msg.content / price for msg in ct_messages])
47 | if demand < self.possession(capital_type):
48 | self.rationing = rationing = 1
49 | else:
50 | self.rationing = rationing = self.possession(capital_type) / demand
51 | for msg in ct_messages:
52 | sell = self.sell(msg.sender,
53 | good=capital_type,
54 | quantity=msg.content / price * rationing,
55 | price=price)
56 | self.sells.append(sell)
57 |
58 | def buying(self):
59 | for final_good in self.final_goods:
60 | for offer in self.get_offers(final_good):
61 | self.accept(offer)
62 |
63 | def money_to_nx(self):
64 | self.give(('netexport', 0), quantity=self.possession('money'), good='money')
65 |
66 | def sales_accounting(self):
67 | self.sales_earning = sum([sell.final_quantity * sell.price for sell in self.sells])
68 | self.sells = []
69 |
70 | def consuming(self):
71 | self.welfare = self.consume_everything()
72 |
--------------------------------------------------------------------------------
/examples/cce/household_welfare.txt:
--------------------------------------------------------------------------------
1 | household welfare
2 | Household welfare is the material
3 | welfare of the representative household
4 | in the economy.
5 |
6 | Generally an introduction of a tax
7 | decreases household welfare as the
8 | household allocates its resources to
9 | maximize its material welfare. When an
10 | individual is taxed, household resources
11 | cannot be allocated as freely, leading
12 | to an inferior outcome.
13 |
14 | However, we are not measuring direct or
15 | indirect benefits of the decrease in
16 | pollution, so it's possible that the
17 | actual welfare increases.
18 |
--------------------------------------------------------------------------------
/examples/cce/intro.txt:
--------------------------------------------------------------------------------
1 | Introduction
2 |
3 | The following graphs show production and prices
4 | for all sectors. For the primary sectors the embedded
5 | CO2 is displayed. For the consumer it shows the
6 | welfare.
7 |
8 |
9 | Secondary sectors don't produce CO2 other than what
10 | is already accounted for in the primary sectors'
11 | CO2 output.
12 |
13 | The first 50 periods of the simulation are not
14 | displayed, as they serve to set up the model. At
15 | period 100, the new policy is implemented. The user
16 | can observe the CO2 output before and after the
17 | introduction of the new tax policy.
18 |
--------------------------------------------------------------------------------
/examples/cce/iotable.py:
--------------------------------------------------------------------------------
1 | import pandas as pd
2 | import numpy as np
3 |
4 |
5 |
6 | def to_iotable(name, rounds=None):
7 | pd.set_option('expand_frame_repr', False)
8 | pd.set_option('precision', 3)
9 | pd.set_option('display.float_format', lambda x: '%.3f' % x)
10 | df = pd.read_csv(name + '/trade___trade.csv')
11 | if rounds is None:
12 | rounds = [max(df['round'])]
13 | q = []
14 | p = []
15 | for round in rounds:
16 | table = df[df['round'] == round]
17 | table.drop(['round', 'index'], axis=1, inplace=True)
18 | grouped_table = table.groupby(['seller', 'buyer'])
19 | quantities = grouped_table.sum()['quantity']
20 | prices = grouped_table.mean()['price']
21 | value = quantities * prices
22 | quantities = quantities.unstack()
23 | quantities = quantities.reindex_axis(['col', 'ele', 'gas', 'o_g', 'oil', 'eis', 'trn', 'roe', 'lab', 'cap', 'government', 'household', 'inv', 'netexport'], axis=1)
24 | quantities = quantities.reindex_axis(['col', 'ele', 'gas', 'o_g', 'oil', 'eis', 'trn', 'roe', 'lab', 'cap', 'government', 'household', 'inv', 'netexport'], axis=0)
25 | quantities = quantities.replace(np.NaN, 0)
26 | quantities['netexport'] = quantities['netexport'] - quantities.ix['netexport']
27 | quantities['sum'] = sum([quantities[name] for name in quantities.columns])
28 | quantities.ix['sum'] = sum([quantities.ix[name] for name in quantities.T.columns])
29 | prices = prices.unstack()
30 | prices = prices.reindex_axis(['col', 'ele', 'gas', 'o_g', 'oil', 'eis', 'trn', 'roe', 'lab', 'cap', 'government', 'household', 'inv', 'netexport'], axis=0)
31 | prices = prices.reindex_axis(['col', 'ele', 'gas', 'o_g', 'oil', 'eis', 'trn', 'roe', 'lab', 'cap', 'government', 'household', 'inv', 'netexport'], axis=1)
32 | prices = prices.replace(np.NaN, 0)
33 |
34 | table['value'] = table['quantity'] * table['price']
35 | #vtable.drop(['round', 'index'], axis=1, inplace=True)
36 | vgrouped_table = table.groupby(['seller', 'buyer'])
37 | values = vgrouped_table.sum()['value']
38 | values = values.unstack()
39 | values = values.reindex_axis(['col', 'ele', 'gas', 'o_g', 'oil', 'eis', 'trn', 'roe', 'lab', 'cap', 'government', 'household', 'inv', 'netexport'], axis=1)
40 | values = values.reindex_axis(['col', 'ele', 'gas', 'o_g', 'oil', 'eis', 'trn', 'roe', 'lab', 'cap', 'government', 'household', 'inv', 'netexport'], axis=0)
41 | values = values.replace(np.NaN, 0)
42 | print('***\tvalues\t***')
43 | print(values)
44 | print('***\tprice\t***')
45 | print(prices)
46 | p.append(prices)
47 | print('***\tquantities\t***')
48 | print(quantities)
49 | q.append(quantities)
50 | print('***\trelative\t***')
51 | print
52 | pd.set_option('display.float_format', lambda x: '%.1f' % (x * 100))
53 | print('p')
54 | print(p[1] / p[0] - 1)
55 | print('q')
56 | print(q[1] / q[0] - 1)
57 | return value
58 |
59 |
60 | def average_price(name, round=99):
61 | pd.set_option('display.float_format', lambda x: '%.25f' % x)
62 | df = pd.read_csv(name + '/trade___trade.csv')
63 | table = df[df['round'] == round]
64 | table.drop(['round', 'index'], axis=1, inplace=True)
65 | grouped_table = table.groupby(['seller', 'buyer'])
66 | prices = grouped_table.mean()['price']
67 | prices = prices.unstack()
68 | prices = prices.replace(0, np.NaN)
69 | mean = prices.mean()
70 | mean = mean.mean()
71 | return mean
72 |
73 |
74 |
75 |
76 |
77 |
78 | if __name__ == '__main__':
79 | value = to_iotable('./result/cce_2016-01-04_11-30')
80 |
--------------------------------------------------------------------------------
/examples/cce/netexport.py:
--------------------------------------------------------------------------------
1 | import abcEconomics as abce
2 |
3 |
4 | class NetExport(abce.Agent):
5 | def init(self):
6 | self.create('money', 0)
7 |
8 | def invest(self):
9 | offers_grouped = self.get_offers_all().values()
10 | offers = []
11 | for os in offers_grouped:
12 | offers.extend(os)
13 | demand = sum([offer.quantity * offer.price for offer in offers if offer.buysell != 98])
14 | if demand < self.possession('money'):
15 | self.rationing = rationing = 1
16 | else:
17 | self.rationing = rationing = self.possession('money') / demand
18 |
19 | for offer in offers:
20 | if offer.buysell == 98:
21 | self.create(offer.good, offer.quantity)
22 | self.accept(offer)
23 | else:
24 | self.accept(offer, offer.quantity * rationing)
25 |
26 | self.give(('household', 0), quantity=self.possession('money'), good='money')
27 |
--------------------------------------------------------------------------------
/examples/cce/optimization_functions.py:
--------------------------------------------------------------------------------
1 | from scipy.optimize import minimize
2 | import numpy as np
3 |
4 |
5 | def F(x, p, b, beta):
6 | return - b * np.prod((x / p) ** beta)
7 |
8 | def optimization(seed_weights,
9 | input_prices,
10 | b,
11 | beta,
12 | method='SLSQP'):
13 |
14 | cons = ({'type': 'eq', 'fun': lambda x: np.array([sum(x) - 1])})
15 |
16 | return minimize(F, seed_weights, args=(input_prices, b, beta), constraints=cons, bounds=[(0, 1)] * len(seed_weights), method=method)
17 |
--------------------------------------------------------------------------------
/examples/cce/sam_to_functions.py:
--------------------------------------------------------------------------------
1 | import csv
2 | from collections import defaultdict
3 | from copy import copy
4 | import numpy as np
5 |
6 | def float_or_zero(value):
7 | try:
8 | return float(value)
9 | except ValueError:
10 | return 0.0
11 |
12 | class Sam():
13 | def __init__(self, name, inputs, outputs, output_tax, consumption, consumers):
14 | """ read the sam matrix into self.entries column sums in self.column_sum
15 |
16 | Table must have index as upper left title, column sum titled sum are not
17 | read. Everything in and after the row that begin with sum is ignored """
18 | self.inputs, self.outputs, self.output_tax = inputs, outputs, output_tax
19 | self.consumption, self.consumers = consumption, consumers
20 |
21 | entries = defaultdict(dict)
22 | with open(name, 'rU') as csvfile:
23 | reader = csv.DictReader(csvfile)
24 | fields = copy(reader.fieldnames)
25 | fields.remove('index')
26 | fields.remove('sum')
27 | for row in reader:
28 | rowindex = row['index']
29 | if rowindex == 'sum':
30 | break
31 | for cellindex, cell in row.items():
32 | if cellindex not in ['index', 'sum']:
33 | entries[rowindex][cellindex] = float_or_zero(cell)
34 |
35 | column_sum = {}
36 | for col in fields:
37 | column_sum[col] = sum(item[col] for item in entries.values())
38 |
39 | self.entries, self.column_sum = dict(entries), column_sum
40 |
41 | def utility_function(self):
42 | """ the utility functions exponents as values in a dict """
43 | entries, column_sum = self.entries, self.column_sum
44 | # output_tax_shares = self.output_tax_shares()
45 |
46 | utility_functions = {}
47 | for consumer in self.consumers:
48 | alphas = {}
49 | for input in self.consumption:
50 | alphas[input] = entries[input][consumer] / column_sum[consumer]
51 | utility_functions[consumer] = alphas
52 |
53 | return utility_functions
54 |
55 | def production_functions(self):
56 | """ returns the the tax adjusted cobb-douglas parameters as a per firm
57 | dictionary of tuples, the
58 | tuple contains the scalar b and a dictionary with the cobb-douglas
59 | coefficients """
60 | entries, column_sum = self.entries, self.column_sum
61 | output_tax_shares = self.output_tax_shares()
62 | betas = defaultdict(dict)
63 | b = {}
64 | production_functions = {}
65 |
66 | for firm in self.outputs:
67 |
68 | for input in self.outputs:
69 | betas[firm][input] = entries[input][firm] / ((1 - output_tax_shares[firm]) * column_sum[firm])
70 |
71 | for input in ['cap', 'lab']:
72 | betas[firm][input] = entries[input][firm] / ((1 - output_tax_shares[firm]) * column_sum[firm])
73 |
74 | b[firm] = (column_sum[firm] /
75 | np.prod([entries[input][firm] ** betas[firm][input]
76 | for input in self.inputs]))
77 |
78 | production_functions[firm] = (b[firm], dict(betas[firm]))
79 |
80 | return production_functions
81 |
82 | def output_tax_shares(self):
83 | """ returns how much of each firms output is taxed in an output tax """
84 | entries, column_sum, output_tax = self.entries, self.column_sum, self.output_tax
85 | output_tax_shares = {}
86 | for firm in self.outputs:
87 | output_tax_shares[firm] = (entries[output_tax][firm] /
88 | (sum([entries[input][firm]
89 | for input in self.inputs]) + entries[output_tax][firm]))
90 | output_tax_shares['cap'] = output_tax_shares['lab'] = 0
91 | return output_tax_shares
92 |
93 | def endowment(self, name):
94 | """ returns the endowment of the whole economy for each factor """
95 | assert name in self.inputs
96 | return self.column_sum[name]
97 |
98 | def endowment_vector(self, column):
99 | """ return the each row entry for a column """
100 | return {row_name: self.entries[row_name][column] for row_name in self.entries}
101 |
102 | def investment_share(self, von, zu):
103 | """ returns """
104 | return self.column_sum[zu] / sum(self.entries[von].values())
105 |
106 | def initial_investment(self, zu):
107 | return self.column_sum[zu]
108 |
109 | def money(self):
110 | return sum([self.column_sum[input] for input in self.inputs])
111 |
112 | def balance_of_payment(self, netexport, investment):
113 | return self.entries[investment][netexport]
114 |
--------------------------------------------------------------------------------
/examples/cce/start.py:
--------------------------------------------------------------------------------
1 | from firm import Firm
2 | from household import Household
3 | from netexport import NetExport
4 | from government import Government
5 | from abcEconomics import Simulation
6 | from collections import OrderedDict, defaultdict
7 | from sam_to_functions import Sam
8 | import iotable
9 |
10 |
11 | title = "Computational Complete Economy Model on Climate Gas Reduction"
12 |
13 | text = """In the Short Run We Are All Dead
14 | In the Short Run We Are All Dead: Non-Equilibrium Dynamics in a Computational General Equilibrium Model.
15 | Studies of the economic impact and mitigation of climate change usually use computable general equilibrium models (CGE). Equilibrium models, as the name suggests, model the economy in equilibrium: the transitions to the equilibrium are ignored. In the time spend outside equilibrium, the economy produces different quantities of goods and pollution, as predicted by the equilibrium model. If the economy in this time outside of the equilibrium produces more climate gases, the predictions are dangerously wrong.
16 | In this paper we present a computational generalization of the Arrow-Debreu general equilibrium model, which is not in equilibrium during the transitions, but converges to the same equilibrium as a CGE model with the same data and assumptions. We call this new class of models Computational Complete Economy models.
17 | Computational Complete Economy models have other interesting applications, for example in international trade, tax policy, and macroeconomics.
18 |
19 | On the left-hand side you can introduce a series of tax policies, the most important being the tax on carbon. This tax is applied to the carbon output of the three sectors that produce raw materials: coal mining, refined petroleum and gas works, and distribution.
20 | """
21 |
22 | simulation_parameters = OrderedDict((('carbon_tax', (0, 0.0, 80.0)),
23 | ('tax_eis', (0.0, 0.012963293555430438 * 100, 100.0)),
24 | ('tax_oil', (0.0, 0.011268228015908086 * 100, 100.0)),
25 | ('tax_trn', (0.0, 0.026571679384158282 * 100, 100.0)),
26 | ('tax_gas', (0.0, 0.02444919587245515 * 100, 100.0)),
27 | ('tax_roe', (0.0, 0.02309537718734039 * 100, 100.0)),
28 | ('tax_ele', (0.0, 0.10978500776587917 * 100, 100.0)),
29 | ('tax_o_g', (0.0, 0.023756906077348067 * 100, 100.0)),
30 | ('tax_col', (0.0, 0.08872377622377624 * 100, 100.0)),
31 | ('tax_change_time', 100),
32 | ('rounds', 200)))
33 |
34 |
35 | names = {'carbon_tax': 'Tax per ton of carbon in US dollars',
36 | 'tax_eis': 'Tax on energy intensive industry sectors in percent',
37 | 'tax_oil': 'Tax on refined petroleum in percent of revenue',
38 | 'tax_trn': 'Tax on transportation in percent of revenue',
39 | 'tax_gas': 'Tax on gas works and distribution oil in percent of revenue',
40 | 'tax_roe': 'Tax on the rest of the economy in percent of revenue',
41 | 'tax_ele': 'Tax on electric power in percent of revenue',
42 | 'tax_o_g': 'Tax on crude oil and gas in percent of revenue',
43 | 'tax_col': 'Tax on coal mining in percent of revenue',
44 | 'tax_change_time': 'Time of policy change',
45 | 'rounds': 'Simulation length'}
46 |
47 | simulation_parameters['trade_logging'] = 'group'
48 |
49 | def main(simulation_parameters):
50 | sam = Sam('climate_square.sam.csv',
51 | inputs=['col', 'ele', 'gas', 'o_g', 'oil', 'eis', 'trn', 'roe', 'lab', 'cap'],
52 | outputs=['col', 'ele', 'gas', 'o_g', 'oil', 'eis', 'trn', 'roe'],
53 | output_tax='tax',
54 | consumption=['col', 'ele', 'gas', 'o_g', 'oil', 'eis', 'trn', 'roe'],
55 | consumers=['hoh'])
56 | """ reads the social accounting matrix and returns coefficients of a cobb-douglas model """
57 | carbon_prod = defaultdict(float)
58 | carbon_prod.update({'col': 2112 * 1e-4,
59 | 'oil': 2439.4 * 1e-4,
60 | 'gas': 1244.3 * 1e-4})
61 | """ this is the co2 output per sector at the base year """
62 | print(sam.output_tax_shares())
63 |
64 | simulation_parameters.update({'name': 'cce',
65 | 'random_seed': None,
66 | 'num_household': 1,
67 | 'num_firms': 1,
68 | 'endowment_FFcap': sam.endowment('cap'),
69 | 'endowment_FFlab': sam.endowment('lab'),
70 | 'final_goods': sam.consumption,
71 | 'capital_types': ['cap', 'lab'],
72 | 'dividends_percent': 0.0,
73 | 'production_functions': sam.production_functions(),
74 | 'consumption_functions': sam.utility_function(),
75 | 'output_tax_shares': sam.output_tax_shares(),
76 | 'money': 2691.2641884030372,
77 | 'inputs': sam.inputs,
78 | 'outputs': sam.outputs,
79 | 'balance_of_payment': sam.balance_of_payment('nx', 'inv'),
80 | 'sam': sam,
81 | 'carbon_prod': carbon_prod,
82 | 'wage_stickiness': 0.5,
83 | 'price_stickiness': 0.5,
84 | 'network_weight_stickiness': 0.5})
85 |
86 | simulation = Simulation(trade_logging='group', processes=1)
87 |
88 | simulation.declare_service('endowment_FFcap', 1, 'cap')
89 | simulation.declare_service('endowment_FFlab', 1, 'lab')
90 | """ every round for every endowment_FFcap the owner gets one good of lab
91 | similar for cap"""
92 |
93 | firms = {good: simulation.build_agents(Firm,
94 | number=simulation_parameters['num_firms'],
95 | group_name=good,
96 | parameters=simulation_parameters)
97 | for good in sam.outputs}
98 | household = simulation.build_agents(Household, 'household', simulation_parameters['num_household'], parameters=simulation_parameters)
99 | netexport = simulation.build_agents(NetExport, 'netexport', 1, parameters=simulation_parameters)
100 | government = simulation.build_agents(Government, 'government', 1, parameters=simulation_parameters)
101 |
102 | firms_and_household = sum(firms.values()) + household
103 | all_firms = sum(firms.values())
104 |
105 | try:
106 | for r in range(simulation_parameters['rounds']):
107 | simulation.advance_round(r)
108 | all_firms.taxes_intervention()
109 | firms_and_household.send_demand()
110 | firms_and_household.selling()
111 | firms_and_household.buying()
112 | household.money_to_nx()
113 | all_firms.production()
114 | all_firms.carbon_taxes()
115 | all_firms.sales_tax()
116 | government.taxes_to_household()
117 | all_firms.international_trade()
118 | all_firms.invest()
119 | netexport.invest()
120 | household.sales_accounting()
121 | all_firms.dividends()
122 | all_firms.change_weights()
123 | all_firms.stats()
124 | household.agg_log(variables=['welfare'])
125 | (firms['col'] + firms['gas'] + firms['oil']).agg_log(
126 | variables=['price', 'produced', 'co2'])
127 |
128 | (firms['ele'] + firms['o_g'] + firms['eis'] + firms['trn'] + firms['roe']).agg_log(
129 | variables=['price', 'produced'])
130 | household.consuming()
131 | except Exception as e:
132 | print(e)
133 |
134 | simulation.finalize()
135 | # raise # put raise for full traceback but no graphs in case of error
136 | iotable.to_iotable(simulation.path, [99, simulation_parameters['rounds'] - 1])
137 | mean_price = iotable.average_price(simulation.path, 99)
138 | print('mean price', mean_price)
139 | return mean_price
140 |
141 | def F(money):
142 | prices = main(float(money))
143 | print("****")
144 | print('money', money)
145 | print('price lvl', prices)
146 | print("****")
147 | return ((1.0 - prices) ** 2) * 100000
148 |
149 | if __name__ == '__main__':
150 | main()
151 | # opt = optimize.minimize_scalar(F, bracket=(2685, 2750), bounds=(2685, 2750), method='brent', options={'xtol': 0.000000000001})
152 | # print(opt)
153 |
--------------------------------------------------------------------------------
/examples/create_and_delete_agents/ball.py:
--------------------------------------------------------------------------------
1 | from abcEconomics.agent import Agent
2 |
3 |
4 | class Ball(Agent):
5 | def init(self):
6 | pass
7 |
8 | def boing(self):
9 | print('*', end="", flush=True)
10 |
--------------------------------------------------------------------------------
/examples/create_and_delete_agents/killer.py:
--------------------------------------------------------------------------------
1 | import abcEconomics as abce
2 |
3 |
4 | class Killer(abce.Agent, abce.Household):
5 | def init(self):
6 | # your agent initialization goes here, not in __init__
7 | pass
8 |
9 | def kill_silent(self):
10 | agent_to_kill = ('victim', self.time)
11 | return agent_to_kill
12 |
13 | def kill_loud(self):
14 | agent_to_kill = ('loudvictim', self.time)
15 | return agent_to_kill
16 |
--------------------------------------------------------------------------------
/examples/create_and_delete_agents/start.py:
--------------------------------------------------------------------------------
1 | import abcEconomics as abce
2 | from ball import Ball
3 | from killer import Killer
4 | from victim import Victim
5 |
6 |
7 | rounds = num_victims = 30
8 |
9 | simulation = abce.Simulation(processes=1)
10 |
11 | print('build Killer')
12 | killer = simulation.build_agents(Killer, 'killer', 1)
13 | print('build Victim')
14 | victims = simulation.build_agents(Victim, 'victim', num_victims)
15 | print('build Victim loudvictim')
16 | loudvictims = simulation.build_agents(Victim, 'loudvictim', num_victims)
17 | print('build AddAgent')
18 | balls = simulation.build_agents(Ball, 'ball', 0)
19 |
20 | for time in range(rounds):
21 | simulation.advance_round(time)
22 | deads = killer.kill_silent()
23 | victims.delete_agents(deads)
24 | deads = killer.kill_loud()
25 | loudvictims.delete_agents(deads)
26 | victims.am_I_dead()
27 | loudvictims.am_I_dead()
28 |
29 | balls.create_agents(Ball, 1)
30 |
31 | simulation.finalize()
32 |
--------------------------------------------------------------------------------
/examples/create_and_delete_agents/victim.py:
--------------------------------------------------------------------------------
1 | import abcEconomics as abce
2 |
3 |
4 | class Victim(abce.Agent, abce.Household):
5 | def init(self):
6 | # your agent initialization goes here, not in __init__
7 | self.count = 1
8 | self.idn = self.id
9 |
10 | def am_I_dead(self):
11 | if self.id < self.time:
12 | raise Exception("should be dead %i" % self.id)
13 |
--------------------------------------------------------------------------------
/examples/mesa_example/model.py:
--------------------------------------------------------------------------------
1 | """ This is a simple demonstration model how to integrate ABCE and mesa.
2 | The model and scheduler specification are taken care of in
3 | ABCE instead of Mesa.
4 |
5 | Based on
6 | https://github.com/projectmesa/mesa/tree/master/examples/boltzmann_wealth_model.
7 |
8 | For further reading, see
9 | [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)
10 | """
11 | import abcEconomics as abce
12 | from mesa.space import MultiGrid
13 | from mesa.datacollection import DataCollector
14 | from moneyagent import MoneyAgent
15 |
16 |
17 | def compute_gini(model):
18 | """ calculates the index of wealth distribution form a list of numbers """
19 | agent_wealths = model.wealths
20 | x = sorted(agent_wealths)
21 | N = len(x)
22 | B = sum(xi * (N - i) for i, xi in enumerate(x)) / (N * sum(x))
23 | return 1 + (1 / N) - 2 * B
24 |
25 |
26 | class MoneyModel(abce.Simulation): # The actual simulation must inherit from Simulation
27 | """ The actual simulation. In order to interoperate with MESA the simulation
28 | needs to be encapsulated in a class. __init__ sets the simulation up. The step
29 | function runs one round of the simulation. """
30 |
31 | def __init__(self, num_agents, x_size, y_size):
32 | super().__init__(name='ABCE and MESA integrated',
33 | processes=1)
34 | # initialization of the base class. MESA integration requires
35 | # single processing
36 | self.grid = MultiGrid(x_size, y_size, True)
37 | self.agents = self.build_agents(MoneyAgent, 'MoneyAgent', num_agents,
38 | grid=self.grid)
39 | # ABCE agents must inherit the MESA grid
40 | self.running = True
41 | # MESA requires this
42 | self.datacollector = DataCollector(
43 | model_reporters={"Gini": compute_gini})
44 | # The data collector collects a certain aggregate value so the graphical
45 | # components can access them
46 |
47 | self.wealths = [0 for _ in range(num_agents)]
48 | self.r = 0
49 |
50 | def step(self):
51 | """ In every step the agent's methods are executed, every set the round
52 | counter needs to be increased by self.next_round() """
53 | self.advance_round(self.r)
54 | self.agents.move()
55 | self.agents.give_money()
56 | self.wealths = self.agents.report_wealth()
57 | # agents report there wealth in a list self.wealth
58 | self.datacollector.collect(self)
59 | # collects the data
60 | self.r += 1
61 |
62 |
63 | if __name__ == '__main__':
64 | """ If you run model.py the simulation is executed without graphical
65 | representation """
66 | money_model = MoneyModel(1000, 20, 50)
67 | for r in range(100):
68 | print(r)
69 | money_model.step()
70 |
--------------------------------------------------------------------------------
/examples/mesa_example/moneyagent.py:
--------------------------------------------------------------------------------
1 | import abcEconomics as abce
2 | import random
3 |
4 |
5 | class MoneyAgent(abce.Agent):
6 | """ agents move randomly on a grid and give_money to another agent in the same cell """
7 |
8 | def init(self, grid):
9 | self.grid = grid
10 | """ the grid on which agents live must be imported """
11 | x = random.randrange(self.grid.width)
12 | y = random.randrange(self.grid.height)
13 | self.pos = (x, y)
14 | self.grid.place_agent(self, (x, y))
15 | self.create('money', random.randrange(2, 10))
16 |
17 | def move(self):
18 | """ moves randomly """
19 | possible_steps = self.grid.get_neighborhood(self.pos,
20 | moore=True,
21 | include_center=False)
22 | new_position = random.choice(possible_steps)
23 | self.grid.move_agent(self, new_position)
24 |
25 | def give_money(self):
26 | """ If the agent has wealth he gives it to cellmates """
27 | cellmates = self.grid.get_cell_list_contents([self.pos])
28 | if len(cellmates) > 1:
29 | other = random.choice(cellmates)
30 | try:
31 | self.give(other.name, good='money', quantity=1)
32 | except abce.NotEnoughGoods:
33 | pass
34 |
35 | def report_wealth(self):
36 | return self['money']
37 |
--------------------------------------------------------------------------------
/examples/mesa_example/start.py:
--------------------------------------------------------------------------------
1 | """ This is a simple demonstration model how to integrate ABCE and mesa.
2 | The model and scheduler specification are taken care of in
3 | ABCE instead of Mesa.
4 |
5 | Based on
6 | https://github.com/projectmesa/mesa/tree/master/examples/boltzmann_wealth_model.
7 |
8 | For further reading, see
9 | [Dragulescu, A and Yakovenko, V. Statistical Mechanics of Money, Income, and Wealth: A Short Survey.
10 | November, 2002](http://arxiv.org/pdf/cond-mat/0211175v1.pdf)
11 | """
12 | from model import MoneyModel
13 | from mesa.visualization.modules import CanvasGrid
14 | from mesa.visualization.ModularVisualization import ModularServer
15 | from mesa.visualization.modules import ChartModule
16 |
17 |
18 | def agent_portrayal(agent):
19 | """ This function returns a big red circle, when an agent is wealthy and a
20 | small gray circle when he is not """
21 | portrayal = {"Shape": "circle",
22 | "Filled": "true",
23 | "r": 0.5}
24 |
25 | if agent.report_wealth() > 0:
26 | portrayal["Color"] = "red"
27 | portrayal["Layer"] = 0
28 | else:
29 | portrayal["Color"] = "grey"
30 | portrayal["Layer"] = 1
31 | portrayal["r"] = 0.2
32 | return portrayal
33 |
34 |
35 | def main(x_size, y_size):
36 | """ This function sets up a canvas to graphically represent the model 'MoneyModel'
37 | and a chart, than it runs the server and runs the model in model.py in the browser """
38 | grid = CanvasGrid(agent_portrayal, x_size, y_size, 500, 500)
39 |
40 | chart = ChartModule([{"Label": "Gini",
41 | "Color": "Black"}],
42 | data_collector_name='datacollector')
43 | # the simulation uses a class DataCollector, that collects the data and
44 | # relays it from self.datacollector to the webpage
45 |
46 | server = ModularServer(MoneyModel,
47 | [grid, chart],
48 | "ABCE and MESA integrated",
49 | {'num_agents': 1000, 'x_size': x_size, 'y_size': y_size})
50 | server.port = 8534 # change this number if address is in use
51 | server.launch()
52 |
53 |
54 | if __name__ == '__main__':
55 | main(25, 25)
56 |
--------------------------------------------------------------------------------
/examples/monetary_shocks/centralbank.py:
--------------------------------------------------------------------------------
1 | import abcEconomics as abce
2 | import random
3 | from optimization_functions import optimization
4 |
5 |
6 | class CentralBank(abce.Agent):
7 | def init(self, time_of_intervention, percentage_injection, percentage_beneficiaries):
8 | self.time_of_intervention = time_of_intervention
9 | self.percentage_injection = percentage_injection
10 | self.percentage_beneficiaries = percentage_beneficiaries
11 |
12 | def intervention(self):
13 | if self.time == self.time_of_intervention:
14 | print('intervention', self.percentage_injection)
15 | messages = self.get_messages('grant')
16 | money_in_the_economy = sum([msg.content['money'] for msg in messages])
17 | injection = money_in_the_economy * self.percentage_injection
18 | beneficiaries = random.sample(messages, int(len(messages) * self.percentage_beneficiaries))
19 | money_beneficiaries = sum([beneficiary.content['money'] for beneficiary in beneficiaries])
20 |
21 | for msg in beneficiaries:
22 | self.create('money', injection)
23 | self.give(msg.sender,
24 | good='money',
25 | quantity=msg.content['money'] / money_beneficiaries * injection)
26 |
--------------------------------------------------------------------------------
/examples/monetary_shocks/firm.py:
--------------------------------------------------------------------------------
1 | #pylint: disable=W0201
2 | import random
3 | from copy import copy
4 | import numpy as np
5 | import abcEconomics as abce
6 | from abcEconomics import NotEnoughGoods
7 | from optimization_functions import optimization
8 |
9 |
10 | epsilon = 1 / 10000
11 |
12 |
13 | def good_from_id(idn):
14 | return 'g%i' % idn
15 |
16 | def normalized_random(length):
17 | random_values = [random.uniform(0.1, 0.9) for _ in range(length)]
18 | sum_values = sum(random_values)
19 | return np.array([v / sum_values for v in random_values])
20 |
21 | class Firm(abce.Agent, abce.Firm):
22 | def init(self, num_firms, alpha, gamma, price_stickiness, dividends_percent, network_weight_stickiness, time_of_intervention,
23 | neighbors, **trash):
24 | self.num_firms = num_firms
25 | self.alpha = alpha
26 | self.gamma = gamma
27 | self.price_stickiness = price_stickiness
28 | self.dividends_percent = dividends_percent
29 | self.network_weight_stickiness = network_weight_stickiness
30 | self.time_of_intervention = time_of_intervention
31 |
32 | self.neighbors = neighbors
33 | self.neighbors_goods = [good_from_id(idn) for idn in self.neighbors]
34 | self.mygood = good_from_id(self.id)
35 | prices = [1.0 for _ in self.neighbors]
36 | prices.append(1.0)
37 | prices = np.array(prices, dtype=float)
38 | self.neighbor_prices = prices[:-1]
39 | self.wage = prices[-1]
40 |
41 | weigth_and_labor_weight = normalized_random(len(prices))
42 | self.seed_weights = weigth_and_labor_weight
43 | self.weights = weigth_and_labor_weight[:-1]
44 | self.labor_weight = weigth_and_labor_weight[-1]
45 |
46 | self.create(self.mygood, 1)
47 | self.create('money', 1)
48 | self.money_1 = self.not_reserved('money')
49 |
50 | self.price = 1
51 | self.profit = 0
52 | self.profit_1 = 0
53 | self.labor_endowment = 0
54 |
55 | def produce(self, input_goods):
56 | """ Produce according to CES production function """
57 | for good in input_goods:
58 | if self.not_reserved(good) < input_goods[good]:
59 | raise NotEnoughGoods(self.name, good, (input_goods[good] - self[good]))
60 |
61 | for good in input_goods:
62 | self.destroy(good, input_goods[good])
63 |
64 | production = input_goods['labor'] ** self.alpha * sum([input_goods[good] ** self.gamma
65 | for good in input_goods
66 | if good not in ['labor', self.mygood]]) ** ((1 - self.alpha) / self.gamma)
67 | self.create(self.mygood, production)
68 | self.produced = production
69 |
70 | def send_demand(self):
71 | """ send nominal demand, according to weights to neighbor """
72 | for neighbor, weight in zip(self.neighbors, self.weights):
73 | self.send_envelope(('firm', neighbor), 'nominal_demand', weight * self.not_reserved("money"))
74 |
75 | self.send_envelope(('household', 0), 'nominal_demand', self.labor_weight * self.not_reserved('money'))
76 |
77 | def selling(self):
78 | """ receive demand from neighbors and consumer;
79 | calculate market_clearing_price, adaped the price slowly
80 | and sell the good to the neighbors, the quantity might
81 | be rationed.
82 | """
83 | messages = self.get_messages('nominal_demand')
84 | nominal_demand = [msg.content for msg in messages]
85 | assert sum(nominal_demand) > 0
86 | assert self.not_reserved(self.mygood) > 0
87 | market_clearing_price = sum(nominal_demand) / self.not_reserved(self.mygood)
88 | self.price = (1 - self.price_stickiness) * market_clearing_price + self.price_stickiness * self.price
89 | print(messages[0].content)
90 | demand = sum([msg.content / self.price for msg in messages])
91 | if demand <= self.not_reserved(self.mygood):
92 | self.rationing = rationing = 1 - epsilon
93 | else:
94 | self.rationing = rationing = self.not_reserved(self.mygood) / demand - epsilon
95 |
96 | for msg in messages:
97 | self.sell(msg.sender, good=self.mygood, quantity=msg.content / self.price * rationing, price=self.price)
98 |
99 | def buying(self):
100 | """ get offers from each neighbor, accept it and update
101 | neighbor_prices and neighbors_goods """
102 | for offers in self.get_offers_all().values():
103 | for offer in offers:
104 | self.accept(offer)
105 | if offer.good == 'labor':
106 | self.wage = offer.price
107 | else:
108 | index = self.neighbors.index(offer.sender_id)
109 | self.neighbor_prices[index] = offer.price
110 | self.neighbors_goods[index] = offer.good
111 |
112 | def production(self):
113 | """ produce using all goods and labor """
114 | input_goods = {good: self.not_reserved(good) for good in self.neighbors_goods + ['labor']}
115 | self.input_goods = copy(input_goods)
116 | self.produce(input_goods)
117 |
118 | def dividends(self):
119 | """ pay dividends to household if profit is positive, calculate profits """
120 | self.profit = self.not_reserved('money') - self.money_1
121 | earnings = max(0, self.profit)
122 | self.give(('household', 0), good='money', quantity=self.dividends_percent * earnings)
123 | self.money_1 = self.not_reserved('money')
124 |
125 | def _change_weights(self, neighbor_prices, seed_weights):
126 | for _ in range(10):
127 | opt = optimization(seed_weights=seed_weights,
128 | input_prices=np.array(neighbor_prices),
129 | wage=self.wage,
130 | gamma=self.gamma,
131 | one_by_gamma=1 / self.gamma,
132 | l=self.alpha,
133 | one_minus_l=1 - self.alpha)
134 | if not opt.success:
135 | print(self.name, opt.message, (len(seed_weights))) # , self.neighbor_prices, self.seed_weights)
136 | seed_weights = normalized_random(len(seed_weights))
137 | else:
138 | break
139 |
140 | optimal_weights_non_labor, optimal_weights_labor = opt.x[:-1], opt.x[-1]
141 | return opt.x, optimal_weights_non_labor, optimal_weights_labor
142 |
143 | def change_weights(self):
144 | optx, optimal_weights_non_labor, optimal_weights_labor = self._change_weights(self.neighbor_prices,
145 | self.seed_weights)
146 | self.seed_weights = optx
147 | if any(self.seed_weights <= 0):
148 | self.seed_weights = normalized_random(len(self.seed_weights))
149 | self.weights = (self.network_weight_stickiness * self.weights +
150 | (1 - self.network_weight_stickiness) * optimal_weights_non_labor)
151 | self.labor_weight = (self.network_weight_stickiness * self.labor_weight +
152 | (1 - self.network_weight_stickiness) * optimal_weights_labor)
153 | summe = np.nextafter(sum(self.weights) + self.labor_weight, 2)
154 | self.weights = np.nextafter(self.weights / summe, 0)
155 | self.labor_weight = np.nextafter(self.labor_weight / summe, 0)
156 |
157 | assert all(self.weights) >= 0, self.weights
158 | assert self.labor_weight > 0
159 | assert 0.99999 < sum(self.weights) + self.labor_weight < 1.00001, (self.weights, self.labor_weight)
160 |
161 | def stats(self):
162 | """ helper for statistics """
163 | if self.not_reserved('money') > epsilon:
164 | self.dead = 0
165 | else:
166 | self.dead = 1
167 | self.inventory = self.not_reserved(self.mygood)
168 | if self.time == self.time_of_intervention:
169 | self.send_envelope(('centralbank', 0), 'grant', {'money': self.not_reserved('money')})
170 |
--------------------------------------------------------------------------------
/examples/monetary_shocks/graphs.py:
--------------------------------------------------------------------------------
1 | __author__ = 'taghawi'
2 | import pandas as pd
3 | import seaborn as sb
4 | import matplotlib.pyplot as plt
5 | from ggplot import *
6 |
7 |
8 | def graph():
9 | # centralbank = pd.read_csv('aggregate_centralbank.csv').ix[20:]
10 | firm = pd.read_csv('aggregate_firm.csv').ix[400:600]
11 | hh = pd.read_csv('aggregate_household.csv').ix[400:600]
12 |
13 | fig, ax = plt.subplots(nrows=3, ncols=3)
14 |
15 | ax[0][0].set_title('money / dead (red)')
16 | ax2 = ax[0][0].twinx()
17 | sb.tsplot(data=firm['dead'], ax=ax2)
18 | sb.tsplot(data=firm['money'], ax=ax[0][0], color='r')
19 |
20 | ax[0][1].set_title('price (mean) / money (red)')
21 | ax2 = ax[0][1].twinx()
22 | sb.tsplot(data=firm['price_mean'], ax=ax2)
23 | sb.tsplot(data=firm['money'], ax=ax[0][1], color='r')
24 |
25 | ax[1][1].set_title('produced')
26 | ax2 = ax[1][1].twinx()
27 | sb.tsplot(data=firm['produced'], ax=ax2)
28 | sb.tsplot(data=firm['money'], ax=ax[1][1], color='r')
29 |
30 | ax[1][0].set_title('price_std')
31 | ax2 = ax[1][0].twinx()
32 | sb.tsplot(data=firm['price_std'], ax=ax2)
33 | sb.tsplot(data=firm['money'], ax=ax[1][0], color='r')
34 |
35 | ax[2][0].set_title('price coef of variation')
36 | ax2 = ax[2][0].twinx()
37 | sb.tsplot(data=firm['price_std'] / firm['price_mean'], ax=ax2)
38 | sb.tsplot(data=firm['money'], ax=ax[2][0], color='r')
39 |
40 | ax[2][1].set_title('inventory_change')
41 | ax2 = ax[2][1].twinx()
42 | sb.tsplot(data=firm['inventory'], ax=ax2)
43 | sb.tsplot(data=firm['money'], ax=ax[2][1], color='r')
44 |
45 | ax[0][2].set_title('household money / firm money')
46 | ax2 = ax[0][2].twinx()
47 | sb.tsplot(data=hh['money'], ax=ax2)
48 | sb.tsplot(data=firm['money'], ax=ax[0][2], color='r')
49 |
50 | ax[1][2].set_title('firm rationing / firm money')
51 | ax2 = ax[1][2].twinx()
52 | sb.tsplot(data=firm['rationing_mean'], ax=ax2)
53 | sb.tsplot(data=firm['money'], ax=ax[1][2], color='r')
54 |
55 | ax[2][2].set_title('hh rationing / firm money')
56 | ax2 = ax[2][2].twinx()
57 | sb.tsplot(data=hh['rationing_mean'], ax=ax2)
58 | sb.tsplot(data=firm['money'], ax=ax[2][2], color='r')
59 |
60 |
61 | try:
62 | firm = pd.read_csv('panel_firm.csv')[200:]
63 | print(ggplot(aes('round', 'price'), data=firm[firm['id'] < 100]) + geom_line() + facet_wrap('id'))
64 | print(ggplot(aes('round', 'price'), data=firm[firm['id'] > 100]) + geom_line() + facet_wrap('id'))
65 | except IOError:
66 | pass
67 |
68 | try:
69 | sb.plt.savefig('/Users/taghawi/Desktop/foo.png', bbox_inches='tight', dpi=300)
70 | except IOError:
71 | pass
72 | try:
73 | sb.plt.savefig('/home/taghawi/Desktop/foo.png', bbox_inches='tight', dpi=300)
74 | except IOError:
75 | pass
76 | sb.plt.show()
77 |
78 | if __name__ == '__main__':
79 | graph()
80 |
--------------------------------------------------------------------------------
/examples/monetary_shocks/household.py:
--------------------------------------------------------------------------------
1 | import abcEconomics as abce
2 | epsilon = 1 / 10000
3 |
4 |
5 | class Household(abce.Agent, abce.Household):
6 | def init(self, num_firms, wage_stickiness):
7 | self.num_firms = num_firms
8 | self.wage_stickiness = wage_stickiness
9 | self.cdf = self.create_cobb_douglas_utility_function({'g%i' % i: 1 / num_firms for i in range(num_firms)})
10 | self.create('money', 1)
11 | self.labor_endowment = 1
12 | self.utility = 0
13 | self.wage = 0.5
14 |
15 | def send_demand(self):
16 | for i in range(self.num_firms):
17 | self.send_envelope(('firm', i), 'nominal_demand', 1 / self.num_firms * self.not_reserved("money"))
18 |
19 | def selling(self):
20 | """ receive demand from neighbors and consumer;
21 | calculate market_clearing_price, adaped the price slowly
22 | and sell the good to the neighbors, the quantity might
23 | be rationed.
24 | """
25 | messages = self.get_messages('nominal_demand')
26 | nominal_demand = [msg.content for msg in messages]
27 | market_clearing_price = sum(nominal_demand) / self.not_reserved('labor')
28 | self.wage = (1 - self.wage_stickiness) * market_clearing_price + self.wage_stickiness * self.wage
29 | demand = sum([msg.content / self.wage for msg in messages])
30 | if demand <= self.not_reserved('labor'):
31 | self.rationing = rationing = 1 - epsilon
32 | else:
33 | self.rationing = rationing = max(0, self.not_reserved('labor') / demand - epsilon)
34 |
35 | for msg in messages:
36 | self.sell(msg.sender, good='labor', quantity=msg.content / self.wage * rationing, price=self.wage)
37 |
38 | def buying(self):
39 | for neighbor in range(self.num_firms):
40 | for offer in self.get_offers('g%i' % neighbor):
41 | self.accept(offer)
42 |
43 | def consuming(self):
44 | self.utility = self.consume(self.cdf, self.possessions())
45 |
--------------------------------------------------------------------------------
/examples/monetary_shocks/optimization_functions.py:
--------------------------------------------------------------------------------
1 | from scipy.optimize import minimize
2 | from numba import jit
3 |
4 |
5 | # on the next cython update try cdef double F(..)
6 | @jit
7 | def F(xx, prices_non_labor, wage, gamma, one_by_gamma, l, one_minus_l):
8 | price_labor = xx[-1]
9 |
10 | summation = ((xx[:-1] / prices_non_labor) ** gamma).sum()
11 |
12 | non_labor = summation ** one_by_gamma
13 | labor = price_labor / wage
14 | # labor and non-labor inputs together
15 | value = (labor ** l) * (non_labor ** one_minus_l)
16 | value = value / 1000000000
17 | return - float(value)
18 |
19 |
20 | def optimization(seed_weights,
21 | input_prices,
22 | wage,
23 | gamma,
24 | one_by_gamma,
25 | l,
26 | one_minus_l):
27 |
28 | args = (input_prices,
29 | wage,
30 | gamma,
31 | one_by_gamma,
32 | l,
33 | one_minus_l)
34 |
35 | cons = ({'type': 'eq', 'fun': lambda x: 1 - x.sum()})
36 | bnds = [(0, 1) for _ in range(len(seed_weights))]
37 |
38 | return minimize(F, seed_weights, args=args, constraints=cons, bounds=bnds, method='SLSQP',
39 | options={'maxiter': 100000})
40 |
--------------------------------------------------------------------------------
/examples/monetary_shocks/publication_graphs.py:
--------------------------------------------------------------------------------
1 | __author__ = 'taghawi'
2 | import pandas as pd
3 | import seaborn as sb
4 | import matplotlib.pyplot as plt
5 | from ggplot import *
6 | import numpy as np
7 | from os import chdir
8 | import os
9 |
10 | directory = './result'
11 | all_subdirs = [os.path.join(directory, name)
12 | for name in os.listdir(directory)
13 | if os.path.isdir(os.path.join(directory, name))]
14 |
15 | files = dict(zip(all_subdirs, all_subdirs))
16 |
17 | def graph(name, show):
18 | # centralbank = pd.read_csv('aggregate_centralbank.csv').ix[20:]
19 | firm = pd.read_csv('aggregate_firm.csv').ix[100:300]
20 |
21 | fig, ax = plt.subplots(nrows=3)
22 | fig.set_size_inches(18.5, 10.5)
23 | fig.suptitle(name, fontsize=20)
24 | for x in range(len(ax)):
25 | ax[x].get_xaxis().get_major_formatter().set_useOffset(False)
26 | ax[x].get_xaxis().get_major_formatter().set_scientific(False)
27 |
28 | ax[x].get_yaxis().get_major_formatter().set_useOffset(False)
29 | ax[x].get_yaxis().get_major_formatter().set_scientific(False)
30 |
31 | ax[0].set_title('Price Level')
32 | sb.tsplot(data=firm['price_mean'], ax=ax[0])
33 |
34 | ax[1].set_title('Output')
35 | sb.tsplot(data=firm['produced'], ax=ax[1])
36 |
37 | ax[2].set_title('Coefficient of Variation - Price')
38 | sb.tsplot(data=firm['price_std'] / firm['price_mean'], ax=ax[2])
39 |
40 | try:
41 | sb.plt.savefig('../%s.png' % name, bbox_inches='tight', dpi=150)
42 | except IOError:
43 | pass
44 | try:
45 | sb.plt.savefig('../%s.png' % name, bbox_inches='tight', dpi=150)
46 | except IOError:
47 | pass
48 | if show:
49 | sb.plt.show()
50 |
51 | def bins(name, show):
52 | firms = pd.read_csv('panel_firm.csv').ix[100:]
53 | agg = pd.read_csv('aggregate_firm.csv').ix[100:]
54 | agg['cof'] = agg['price_std'] / agg['price_mean']
55 | agg['cof'] = agg['price_std'] / agg['price_mean']
56 | bin_before_n = 99
57 | bin_after_n = np.argmax(agg['cof'])
58 |
59 | bin_before = firms[firms['round'] == bin_before_n]
60 | bin_after = firms[firms['round'] == bin_after_n]
61 |
62 | bin_before = np.array(bin_before['price'].tolist())
63 | bin_after = np.array(bin_after['price'].tolist())
64 |
65 | bin = bin_after - bin_before
66 |
67 | bin = pd.DataFrame(bin)
68 |
69 | plt = ggplot(bin, aes(0)) + stat_bin() + ggtitle(name)
70 | if show:
71 | print(plt)
72 | ggsave(filename='../bin_%s.png' % name, plot=plt, dpi=150)
73 |
74 | if __name__ == '__main__':
75 | show = True
76 |
77 | for directory, name in files.iteritems():
78 | chdir(directory)
79 | print(directory)
80 | graph(name, show)
81 | # bins(name, show)
82 | chdir('../..')
83 |
--------------------------------------------------------------------------------
/examples/monetary_shocks/start.py:
--------------------------------------------------------------------------------
1 | from firm import Firm
2 | from household import Household
3 | from centralbank import CentralBank
4 | from abcEconomics import Simulation
5 | from random import shuffle
6 | import networkx
7 | from random import randrange
8 |
9 |
10 | def create_network(num_firms):
11 | """ the firms are placed on a "scale-free network";
12 | generated through the barabasi-albert algorithm """
13 | G = networkx.scale_free_graph(num_firms)
14 | for i in range(num_firms):
15 | if len(G.out_edges(i)) == 0:
16 | G.add_edge(i, randrange(num_firms))
17 | if len(G.in_edges(i)) == 0:
18 | G.add_edge(randrange(num_firms), i)
19 |
20 | mapping = list(range(num_firms))
21 | shuffle(mapping)
22 | mapping = dict(enumerate(mapping))
23 | return G
24 |
25 |
26 | def main():
27 | simulation_parameters = {'name': 'direct_optimization',
28 | 'random_seed': None,
29 | 'rounds': 500,
30 | 'trade_logging': 'off',
31 | 'num_firms': 250,
32 | 'alpha': 0.3,
33 | 'gamma': 0.8,
34 | 'price_stickiness': 0.0,
35 | 'network_weight_stickiness': 0.0,
36 | 'wage_stickiness': 0.0,
37 | 'dividends_percent': 0.0,
38 | 'percentage_beneficiaries': 0.25,
39 | 'percentage_injection': 0.25,
40 | 'time_of_intervention': 250}
41 | s = Simulation(name=simulation_parameters['name'])
42 |
43 | network = create_network(simulation_parameters['num_firms'])
44 | network = [{'neighbors': network.neighbors(neighbor)} for neighbor in range(simulation_parameters['num_firms'])]
45 | firms = s.build_agents(Firm, 'firm', **simulation_parameters,
46 | agent_parameters=[n for n in network])
47 | household = s.build_agents(Household, 'household', 1,
48 | num_firms=simulation_parameters['num_firms'],
49 | wage_stickiness=simulation_parameters['wage_stickiness'])
50 | centralbank = s.build_agents(CentralBank, 'centralbank', 1,
51 | time_of_intervention=simulation_parameters['time_of_intervention'],
52 | percentage_injection=simulation_parameters['percentage_injection'],
53 | percentage_beneficiaries=simulation_parameters['percentage_beneficiaries'])
54 |
55 | for rnd in range(simulation_parameters['rounds']):
56 | s.advance_round(rnd)
57 | (firms + household).send_demand()
58 | (firms + household).selling()
59 | (firms + household).buying()
60 | firms.production()
61 | firms.dividends()
62 | household.consuming()
63 | firms.change_weights()
64 | firms.stats()
65 | centralbank.intervention()
66 | household.agg_log(goods=['money'],
67 | variables=['utility', 'rationing'])
68 | firms.agg_log(goods=['money'],
69 | variables=['produced', 'profit', 'price', 'dead', 'inventory', 'rationing'])
70 | (household + firms).refresh_services('labor', 'labor_endowment', units=1)
71 |
72 | s.finalize()
73 |
74 |
75 | if __name__ == '__main__':
76 | main()
77 |
--------------------------------------------------------------------------------
/examples/monetary_shocks/systematic_graph.py:
--------------------------------------------------------------------------------
1 | import os
2 | import json
3 | import pandas as pd
4 | from ggplot import *
5 | import statsmodels.formula.api as sm
6 | from statsmodels.sandbox.regression.predstd import wls_prediction_std
7 | import cPickle as pickle
8 |
9 | # nonsensicle seris bank_2015-12-24_16-12 is the strange series 10
10 |
11 | def read_series(throw_away, exclude=[]):
12 | dfs = []
13 | series = 0
14 | # index = []
15 | before_injection_percentage = -1
16 |
17 | directory = os.path.abspath('./result/')
18 | all_subdirs = [os.path.join(directory, name)
19 | for name in os.listdir(directory)
20 | if os.path.isdir(os.path.join(directory, name))]
21 |
22 | for directory in all_subdirs:
23 | try:
24 | with open(directory + '/description.txt', 'r') as myfile:
25 | desc = myfile.read()
26 | try:
27 | desc = json.loads(desc)
28 | except ValueError:
29 | print(desc)
30 | print(directory)
31 | continue
32 |
33 | injection_percentage = desc['percentage_injection']
34 |
35 | data = pd.read_csv(directory + '/aggregate_firm.csv')[throw_away:]
36 | data['injection_percentage'] = injection_percentage
37 | if desc['percentage_injection'] < before_injection_percentage: # color groups
38 | series += 1
39 | data['series'] = series
40 | if series not in exclude:
41 | dfs.append(data)
42 | before_injection_percentage = injection_percentage
43 | except IOError:
44 | print('IOError:', directory)
45 | with open('cache_systematic_graphs.json', 'wb') as jfile:
46 | pickle.dump(dfs, jfile)
47 | return dfs
48 |
49 | def load_series():
50 | with open('cache_systematic_graphs.json', 'rb') as jfile:
51 | dfs = pickle.load(jfile)
52 | return dfs
53 |
54 |
55 | def vector(dfs, column, time=None, time_vector=None, func=None, index=None, color=None):
56 | series = []
57 | i = 0
58 | for df in dfs:
59 | if time is not None:
60 | series.append(df[column][time])
61 | elif func is not None:
62 | series.append(func(df[column]))
63 | elif time_vector is not None:
64 | series.append(df[column][time_vector[i]])
65 | elif index is not None:
66 | series.append(df['injection_percentage'][1])
67 | elif color is not None:
68 | series.append(df['series'][1])
69 | i += 1
70 | return series
71 |
72 | def plot_with_confidence_interval(variable, title):
73 | result = sm.ols(formula=variable + " ~ 1 + index + index2", data=df).fit()
74 | print(result.summary())
75 | df['fittedvalues'] = result.fittedvalues
76 | prstd, iv_l, iv_u = wls_prediction_std(result)
77 | df['iv_u'] = iv_u
78 | df['iv_l'] = iv_l
79 |
80 | graph = (ggplot(aes(x='index', ymax='iv_u', ymin='iv_l'), data=df) +
81 | geom_area(alpha=0.2) +
82 | geom_line(aes(x='index', y='fittedvalues')) +
83 | geom_point(aes(x='index', y=variable)) +
84 | ggtitle(title) +
85 | ylab(title) +
86 | xlab("Injection of Money as a Percentage of Total Money in the Economy"))
87 | if variable == 'long_run_percentage_output':
88 | graph += ylim(-0.01, 0.06)
89 | print(graph)
90 | # ggsave(graph, file_name=variable + ".pdf")
91 |
92 |
93 | if __name__ == '__main__':
94 | throw_away = 1
95 | policy_change = 10 + throw_away
96 | dfs = read_series(throw_away, exclude=[10])
97 | # dfs = load_series()
98 | print('num simulations', len(dfs))
99 | for df in dfs:
100 | df['coef_variation_price'] = df['price_std'] / df['price']
101 |
102 | df = pd.DataFrame()
103 | df['index'] = vector(dfs, 'price_std', index=True)
104 | df['series'] = vector(dfs, 'price_std', color=True)
105 | df['index2'] = df['index'] ** 2
106 |
107 | # coef_variation_price
108 | df['max_coef_variation_price'] = vector(dfs, 'coef_variation_price', func=max)
109 | df['before_coef_variation_price'] = vector(dfs, 'coef_variation_price', time=policy_change)
110 | df['percentage_change_coef_variation_price'] = (df['max_coef_variation_price'] - df['before_coef_variation_price']) / df['before_coef_variation_price']
111 | plot_with_confidence_interval('percentage_change_coef_variation_price', title='Short-Run Percentage Change in the Coefficient of Variation of Price')
112 |
113 | df['long_run_coef_variation_price'] = vector(dfs, 'coef_variation_price', time=49)
114 | df['long_run_percentage_change_coef_variation_price'] = (df['long_run_coef_variation_price'] - df['before_coef_variation_price']) / df['before_coef_variation_price']
115 | plot_with_confidence_interval('long_run_percentage_change_coef_variation_price', title='Long-Run Percentage Change in the Coefficient of Variation of Price')
116 |
117 | # price level
118 | df['max_price'] = vector(dfs, 'price', func=max)
119 | df['before_price'] = vector(dfs, 'price', time=policy_change)
120 | df['percentage_change_price'] = (df['max_price'] - df['before_price']) / df['before_price']
121 | plot_with_confidence_interval('percentage_change_price', title="Short-Run Percentage Change in Price")
122 |
123 | df['long_run_price'] = vector(dfs, 'price', time=49)
124 | df['long_run_percentage_change_price'] = (df['long_run_price'] - df['before_price']) / df['before_price']
125 | plot_with_confidence_interval('long_run_percentage_change_price', title="Long-Run Percentage Change in Price")
126 | # output
127 | df['max_output'] = vector(dfs, 'produced', func=max)
128 | df['before_output'] = vector(dfs, 'produced', time=policy_change)
129 | df['percentage_change_output'] = (df['max_output'] - df['before_output']) / df['before_output']
130 | graph = (ggplot(aes(x='index', y='percentage_change_output'), data=df) + geom_point() + geom_smooth() +
131 | ggtitle("Short-Run Percentage Change of Output") +
132 | ylab("Percentage Change of Output") +
133 | xlab("Injection of Money as a Percentage of Total Money in the Economy"))
134 | print(graph)
135 | # ggsave(graph, 'percentage_change_output.pdf' )
136 |
137 | df['long_run_output'] = vector(dfs, 'produced', time=49)
138 | df['long_run_percentage_output'] = (df['long_run_output'] - df['before_output']) / df['before_output']
139 | graph = (ggplot(aes(x='index', y='long_run_percentage_output'), data=df) + geom_point() + geom_smooth() +
140 | ggtitle("Long-Run Percentage Change in Output") +
141 | ylab("Percentage Change of Output") +
142 | xlab("Injection of Money as a Percentage of Total Money in the Economy"))
143 | print(graph)
144 | df.to_csv('systematic.csv')
145 |
--------------------------------------------------------------------------------
/examples/monetary_shocks/systematic_household_graph.py:
--------------------------------------------------------------------------------
1 | import os
2 | import json
3 | import pandas as pd
4 | from ggplot import *
5 | import statsmodels.formula.api as sm
6 | from statsmodels.sandbox.regression.predstd import wls_prediction_std
7 | import cPickle as pickle
8 |
9 |
10 | def read_series(throw_away, exclude=[]):
11 | dfs = []
12 | series = 0
13 | before_injection_percentage = -1
14 |
15 | directory = os.path.abspath('./result/')
16 | all_subdirs = [os.path.join(directory, name)
17 | for name in os.listdir(directory)
18 | if os.path.isdir(os.path.join(directory, name))]
19 |
20 | for directory in all_subdirs:
21 | try:
22 | with open(directory + '/description.txt', 'r') as myfile:
23 | desc = myfile.read()
24 | try:
25 | desc = json.loads(desc)
26 | except ValueError:
27 | print(desc)
28 | print(directory)
29 | continue
30 |
31 | injection_percentage = desc['percentage_injection']
32 |
33 | data = pd.read_csv(directory + '/aggregate_household.csv')[throw_away:]
34 | data['injection_percentage'] = injection_percentage
35 | if desc['percentage_injection'] < before_injection_percentage: # color groups
36 | series += 1
37 | data['series'] = series
38 | if series not in exclude:
39 | dfs.append(data)
40 | before_injection_percentage = injection_percentage
41 | except IOError:
42 | print('IOError:', directory)
43 | with open('cache_systematic_graphs.json', 'wb') as jfile:
44 | pickle.dump(dfs, jfile)
45 | return dfs
46 |
47 | def load_series():
48 | with open('cache_systematic_household_graphs.json', 'rb') as jfile:
49 | dfs = pickle.load(jfile)
50 | return dfs
51 |
52 | def vector(dfs, column, time=None, time_vector=None, func=None, index=None, color=None):
53 | series = []
54 | i = 0
55 | for df in dfs:
56 | if time is not None:
57 | series.append(df[column][time])
58 | elif func is not None:
59 | series.append(func(df[column]))
60 | elif time_vector is not None:
61 | series.append(df[column][time_vector[i]])
62 | elif index is not None:
63 | series.append(df['injection_percentage'][1])
64 | elif color is not None:
65 | series.append(df['series'][1])
66 | i += 1
67 | return series
68 |
69 | def plot_with_confidence_interval(variable, title, ylim=None):
70 | result = sm.ols(formula=variable + " ~ 1 + index + index2", data=df).fit()
71 | print(result.summary())
72 | df['fittedvalues'] = result.fittedvalues
73 | prstd, iv_l, iv_u = wls_prediction_std(result)
74 | df['iv_u'] = iv_u
75 | df['iv_l'] = iv_l
76 |
77 | graph = (ggplot(aes(x='index', ymax='iv_u', ymin='iv_l'), data=df) +
78 | geom_area(alpha=0.2) +
79 | geom_line(aes(x='index', y='fittedvalues')) +
80 | geom_point(aes(x='index', y=variable)) +
81 | ggtitle(title) +
82 | ylab(title) +
83 | xlab("Injection of Money as a Percentage of Total Money in the Economy"))
84 | if ylim is not None:
85 | graph += ylim
86 | print(graph)
87 | ggsave(plot=graph, filename=variable + ".pdf")
88 | ggsave(plot=graph, filename=variable + ".png")
89 |
90 |
91 | if __name__ == '__main__':
92 | throw_away = 1
93 | policy_change = 10 + throw_away
94 | dfs = read_series(throw_away, exclude=[10])
95 | # dfs = load_series()
96 | dfs = dfs[:int(len(dfs) / 51) * 51]
97 |
98 | print('num simulations', len(dfs))
99 |
100 | df = pd.DataFrame()
101 | df['index'] = vector(dfs, 'utitily', index=True)
102 | df['series'] = vector(dfs, 'utitily', color=True)
103 | df['index2'] = df['index'] ** 2
104 |
105 | # coef_variation_price
106 | df['max_utility'] = vector(dfs, 'utility', func=max)
107 | df['before_utility'] = vector(dfs, 'utility', time=policy_change)
108 | df['percentage_change_utility'] = (df['max_utility'] - df['before_utility']) / df['before_utility']
109 | plot_with_confidence_interval('percentage_change_utility', title='Short-Run Percentage Change in Utility')
110 |
111 | df['long_run_utility'] = vector(dfs, 'utility', time=49)
112 | df['long_run_percentage_change_utility'] = (df['long_run_utility'] - df['before_utility']) / df['before_utility']
113 | plot_with_confidence_interval('long_run_percentage_change_utility', title='Long-Run Percentage Change in Utility',
114 | ylim=scale_y_continuous(limits=(-0.02, 0.08),
115 | breaks=[-0.02, 0, 0.02, 0.04, 0.06, 0.08],
116 | labels=(-0.02, 0, 0.02, 0.04, 0.06, 0.08)))
117 |
118 | df.to_csv('systematic_utility.csv')
119 |
--------------------------------------------------------------------------------
/examples/one_household_one_firm_with_logic/firm.py:
--------------------------------------------------------------------------------
1 | from __future__ import division # makes division work correctly
2 | import abcEconomics as abce
3 | import random
4 |
5 |
6 | class Firm(abce.Agent, abce.Firm):
7 | def init(self):
8 | """ 1. Gets an initial amount of money
9 | 2. create a cobb_douglas function: GOOD = 1 * labor ** 1.
10 | """
11 | self.create('money', 1000)
12 | self.mygood = "GOOD%i" % self.id # GOOD1 if self.id == 1
13 | self.create_cobb_douglas(self.mygood, 1, {"labor": 1})
14 | self.price = random.random() * 2
15 | self.inventory = 0
16 |
17 | def buy_labor(self):
18 | """ receives all labor offers and accepts them one by one """
19 | oo = self.get_offers("labor")
20 | for offer in oo:
21 | self.accept(offer, min(offer.quantity, self.possession('money')))
22 |
23 | def production(self):
24 | """ uses all labor that is available and produces
25 | according to the set cobb_douglas function """
26 | self.produce_use_everything()
27 |
28 | def quotes(self):
29 | self.send(('household', 0), 'quote', (self.mygood, self.price))
30 |
31 | def sell_goods(self):
32 | """ offers one unit of labor to firm 0, for the price of 1 "money" """
33 | oo = self.get_offers(self.mygood)
34 | for offer in oo:
35 | self.accept(offer, min(offer.quantity,
36 | self.possession(self.mygood)))
37 |
38 | def adjust_price(self):
39 | self.inventory = self.possession(self.mygood)
40 | if self.inventory < 4:
41 | self.price += random.random() * 0.01 # random number [0, 0.1]
42 | if self.inventory > 6:
43 | self.price = max(0.01, self.price - random.random() * 0.01) # random number [0, 0.1]
44 |
--------------------------------------------------------------------------------
/examples/one_household_one_firm_with_logic/household.py:
--------------------------------------------------------------------------------
1 | from __future__ import division # makes division work correctly
2 | from builtins import range
3 | import abcEconomics as abce
4 |
5 |
6 | class Household(abce.Agent, abce.Household):
7 | def init(self, num_firms):
8 | """ 1. labor_endowment, which produces, because of w.declare_resource(...)
9 | in start.py one unit of labor per month
10 | 2. Sets the utility function to utility = consumption of good "GOOD"
11 | """
12 | self.create('adult', 1)
13 | self.num_firms = num_firms
14 | self.alpha = alpha = 1 / self.num_firms
15 | cd = {"GOOD%i" % i: alpha for i in range(self.num_firms)}
16 | # creates {GOOD1: 1/3, GOOD2: 1/3, GOOD3: 1/3}
17 | self.set_cobb_douglas_utility_function(cd)
18 | self.current_utiliy = 0
19 |
20 | def sell_labor(self):
21 | """ offers one unit of labor to firm 0, for the price of 1 "money" """
22 | for i in range(self.num_firms):
23 | self.sell(('firm', i),
24 | good="labor",
25 | quantity=1 / self.num_firms,
26 | price=1)
27 |
28 | def buy_goods(self):
29 | """ receives the offers and accepts them one by one """
30 | money = self.possession("money")
31 | quotes = self.get_messages('quote')
32 | for quote in quotes:
33 | price = quote.content[1]
34 | self.buy(quote.sender,
35 | good=quote.content[0],
36 | quantity=self.alpha * money / price,
37 | price=price)
38 |
39 | def consumption(self):
40 | """ consumes_everything and logs the aggregate utility. current_utiliy
41 | """
42 | self.current_utiliy = self.consume_everything()
43 | self.log('HH', self.current_utiliy)
44 |
--------------------------------------------------------------------------------
/examples/one_household_one_firm_with_logic/start.py:
--------------------------------------------------------------------------------
1 | """ 1. declared the timeline
2 | 2. build one Household and one Firm follow_agent
3 | 3. For every labor_endowment an agent has he gets one trade or usable labor
4 | per round. If it is not used at the end of the round it disapears.
5 | 4. Firms' and Households' possesions are monitored ot the points marked in
6 | timeline.
7 | """
8 |
9 | from abcEconomics import Simulation
10 | from firm import Firm
11 | from household import Household
12 |
13 | parameters = {'name': '2x2',
14 | 'random_seed': None,
15 | 'rounds': 2500,
16 | 'num_firms': 10}
17 |
18 | def main(parameters):
19 | simulation = Simulation(processes=1)
20 |
21 | firms = simulation.build_agents(
22 | Firm, 'firm', number=parameters['num_firms'])
23 | households = simulation.build_agents(
24 | Household, 'household', number=1, parameters=parameters)
25 |
26 | try:
27 | for rnd in range(parameters['rounds']):
28 | simulation.advance_round(rnd)
29 | households.refresh_services('labor', derived_from='adult', units=1)
30 | households.sell_labor()
31 | firms.buy_labor()
32 | firms.production()
33 | firms.panel_log(possessions=['money', 'GOOD'],
34 | variables=['price', 'inventory'])
35 | firms.quotes()
36 | households.buy_goods()
37 | firms.sell_goods()
38 | households.agg_log(possessions=['money', 'GOOD'],
39 | variables=['current_utiliy'])
40 | households.consumption()
41 | firms.adjust_price()
42 | except Exception as e:
43 | print(e)
44 | simulation.finalize()
45 |
46 |
47 | if __name__ == '__main__':
48 | main(parameters)
49 |
--------------------------------------------------------------------------------
/examples/pid_controller/analytical/agents_parameters.csv:
--------------------------------------------------------------------------------
1 | agent_class number
2 | firm 5
3 | household 5
4 |
--------------------------------------------------------------------------------
/examples/pid_controller/analytical/firm.py:
--------------------------------------------------------------------------------
1 | # pylint: disable=C0103
2 | """ This firm uses a PID controller to set the highest possible price at which
3 | all it's goods can be sold.
4 | """
5 | from __future__ import division
6 | import abcEconomics as abce
7 | import numpy as np
8 | from picontroller import PiController
9 |
10 |
11 | np.set_printoptions(suppress=True)
12 |
13 |
14 | class Firm(abce.Agent, abce.Firm):
15 | def init(self):
16 | self.price_controller = PiController(0.001, 0.015, positive=True)
17 | self.production_controller = PiController(0.001, 0.015, positive=True)
18 | self.price = self.price_1 = 100
19 | self.total_orders = 0
20 | self.dX = np.ones((20, 4))
21 | self.dy = np.ones(20)
22 | self.X = np.ones((20, 2))
23 | self.y = np.ones(20)
24 | self.L = 4
25 | self.L_1 = 0
26 |
27 | def my_production(self):
28 | """ produce missing cookies """
29 | self.create('cookies', self.L)
30 | self.cookies_before = self.possession('cookies')
31 | self.log('cookies', {'created': self.L,
32 | 'inventory': self.possession('cookies')})
33 | self.y[self.round % 20] = (self.price)
34 | self.dy[self.round % 20] = (self.price - self.price_1)
35 |
36 | def selling(self):
37 | self.offer = self.sell(('market', 0), 'cookies',
38 | self.possession('cookies'), self.price)
39 |
40 | def adjust_price(self):
41 | self.total_orders = self.get_messages('demand')[0].content
42 | self.dX[self.round % 20] = [1, self.L, self.price_1, self.L_1]
43 | self.X[self.round % 20] = [1, self.L]
44 |
45 | self.price_1 = self.price
46 | error = self.total_orders - self.cookies_before
47 | self.price = self.price_controller.update(error)
48 |
49 | self.log('price', {'price': self.price,
50 | 'error_cum': self.price_controller.error_cum,
51 | 'error': error,
52 | 'total_orders': self.total_orders})
53 |
54 | def adjust_quantity(self):
55 | # uw ought to be 1 * L for monopolist, 0 for competitive
56 | # up ought to be -1 * L for monopolist, 0 for competitive
57 | if self.round % 20 == 19:
58 |
59 | # monopolist
60 | uw = 1 * self.L
61 | up = -1 * self.L
62 | w = 14 + self.L
63 | p = self.price
64 |
65 | error = p + up - w - uw
66 | self.L = self.production_controller.update(error)
67 |
68 | self.log('production', {'error_cum': self.production_controller.error_cum,
69 | 'error': error})
70 |
--------------------------------------------------------------------------------
/examples/pid_controller/analytical/market.py:
--------------------------------------------------------------------------------
1 | """ The market agent
2 | has the demand function q = 102 - p
3 | """
4 | from __future__ import division
5 | import abcEconomics as abce
6 |
7 |
8 | class Market(abce.Agent, abce.Household):
9 | def init(self):
10 | self.set_cobb_douglas_utility_function({'cookies': 1})
11 |
12 | def buying(self):
13 | """ buy a cookies if it is smaller then self.id * 10. create enough
14 | money to by one cookies, if its cheap enough (its a partial equilibrium
15 | model)
16 | """
17 | offer = self.get_offers('cookies')[0]
18 | quantity = 102 - offer.price
19 | self.message('firm', 0, 'demand', quantity)
20 | if quantity < 0:
21 | quantity = 0
22 | if quantity > offer.quantity:
23 | quantity = offer.quantity
24 | self.create('money', quantity * offer.price - self.possession('money'))
25 | self.accept(offer, quantity)
26 |
27 | def consumption(self):
28 | """ consume the cookie """
29 | self.consume_everything()
30 |
--------------------------------------------------------------------------------
/examples/pid_controller/analytical/picontroller.py:
--------------------------------------------------------------------------------
1 | from builtins import object
2 |
3 |
4 | class PiController(object):
5 | """ The PiController, learns from an error to set a certain control.
6 | It only produces positive signals.
7 |
8 | Args:
9 | error_parameter:
10 | weight for error
11 | cumulative_error_parameter:
12 | weight for cumulative error
13 | positive:
14 | if true it truncates negative control signals to 0
15 | """
16 |
17 | def __init__(self, error_parameter, cumulative_error_parameter, positive=False):
18 | self.a = error_parameter
19 | self.b = cumulative_error_parameter
20 | self.error_cum = 0
21 |
22 | def _update(self, error):
23 | """ Add a new error to the learning and return the updated control
24 | variable
25 | """
26 | self.error_cum += error
27 | control = self.a * error + self.b * self.error_cum
28 | return control
29 |
30 | def update(self, error):
31 | return max(0, self._update(error))
32 |
--------------------------------------------------------------------------------
/examples/pid_controller/analytical/simulation_parameters.csv:
--------------------------------------------------------------------------------
1 | # name random_seed num_rounds trade_logging trade_repetitions
2 | 1 test_sim_1_ None 3000 individual 20
3 |
--------------------------------------------------------------------------------
/examples/pid_controller/analytical/start.py:
--------------------------------------------------------------------------------
1 | """ A simulation of the first Model of Ernesto Carrella's paper:
2 | Sticky Prices Microfoundations in a Agent Based Supply Chain
3 | Section 4 Firms and Production
4 |
5 | Here we have one firm and one market agent. The market agent
6 | has the demand function q = 102 - p
7 |
8 | """
9 | from __future__ import division
10 | from multiprocessing import freeze_support
11 | from firm import Firm
12 | from market import Market
13 | from abcEconomics import Simulation
14 |
15 |
16 | simulation_parameters = {'name': "analytical",
17 | 'random_seed': None,
18 | 'rounds': 3000}
19 |
20 |
21 | def main(simulation_parameters):
22 | s = Simulation()
23 |
24 | firms = s.build_agents(
25 | Firm, 'firm', parameters=simulation_parameters, number=1)
26 | market = s.build_agents(
27 | Market, 'market', parameters=simulation_parameters, number=1)
28 | for r in range(simulation_parameters['rounds']):
29 | s.advance_round(r)
30 | firms.my_production()
31 | firms.selling()
32 | market.buying()
33 | firms.adjust_price()
34 | firms.adjust_quantity()
35 | market.consumption()
36 | s.finalize()
37 |
38 |
39 | if __name__ == '__main__':
40 | main(simulation_parameters)
41 |
--------------------------------------------------------------------------------
/examples/pid_controller/full_pi_controlled/firm.py:
--------------------------------------------------------------------------------
1 | # pylint: disable=C0103, W0142, W0613, R0904, R0902, R0901, W0201, W0232, E1101
2 | """ This firm uses a pi controller to set t
3 | """
4 | from __future__ import division
5 | import abcEconomics as abce
6 | import numpy as np
7 | np.set_printoptions(suppress=True)
8 | from picontroller import PiController
9 | from upregression import UPRegression
10 |
11 |
12 | class Firm(abce.Agent, abce.Firm, abce.Quote):
13 | def init(self, simulation_parameters, agent_parameters):
14 | self.price = self.price_1 = 100
15 | self.cookies_before = self.production_target = self.production_target_1 = 100
16 | self.wage = self.wage_1 = 1
17 | self.price_controller = PiController(
18 | 0.01, 0.015, output0=self.price, positive=True)
19 | self.production_controller = PiController(
20 | 0.01, 0.015, output0=self.production_target, positive=True)
21 | self.wage_controller = PiController(
22 | 0.01, 0.015, output0=self.wage, positive=True)
23 | self.up_regression = UPRegression(memory=500)
24 | self.uw_regression = UPRegression(memory=500)
25 | self.set_leontief('cookies', {'labor': 1})
26 |
27 | def quote_hire(self):
28 | """ sends a note to the labor market, that it is willing to pay wage """
29 | self.create('money', self.wage * self.production_target)
30 | self.quote_buy(('labormarket', 0), 'labor',
31 | self.possession('money') / self.wage, self.wage)
32 |
33 | def hire(self):
34 | """ hires enough people to meet production_target*. Lets the pi-
35 | controller adjust the wage according to the shortag / excess of labor.
36 |
37 | *but not more than is offered and he can afford """
38 | offer = self.get_offers('labor')[0]
39 | self.accept(offer, min(offer.quantity, self.possession(
40 | 'money') / self.wage, self.production_target))
41 |
42 | self.wage_1 = self.wage
43 | error = self.production_target - offer.quantity
44 | self.wage = self.wage_controller.update(error)
45 |
46 | self.log('wage', {'wage': self.wage,
47 | 'error_cum': self.wage_controller.error_cum,
48 | 'error': error})
49 |
50 | def my_production(self):
51 | """ produce using all workers cookies """
52 | self.log('production', self.produce_use_everything())
53 | self.log('cookies', {'inventory': self.possession('cookies')})
54 |
55 | def selling(self):
56 | """ offers to sell all cookies """
57 | self.offer = self.sell('market', 0, 'cookies',
58 | self.possession('cookies'), self.price)
59 |
60 | def adjust_price(self):
61 | """ The prices are adjusted according to the change in inventory.
62 | up and uw estimates are updated
63 | """
64 | self.total_orders = self.get_messages('demand')[0].content
65 | self.log('total', {'orders': self.total_orders})
66 | self.up_regression.fit(self.price, self.price_1,
67 | self.production_target, self.production_target_1)
68 | self.uw_regression.fit(self.wage, self.wage_1,
69 | self.production_target, self.production_target_1)
70 |
71 | self.price_1 = self.price
72 | error = -(self.possession('cookies') - self.cookies_before)
73 | self.cookies_before = self.possession('cookies')
74 | self.price = self.price_controller.update(error)
75 |
76 | self.log('price', {'price': self.price, 'error_cum': self.price_controller.error_cum,
77 | 'error': error, 'total_orders': self.total_orders})
78 |
79 | def adjust_quantity(self):
80 | if self.round % 20 == 19 and self.round > 50:
81 | w = self.wage
82 | p = self.price
83 |
84 | up = self.up_regression.predict()
85 | uw = self.uw_regression.predict()
86 |
87 | self.production_target_1 = self.production_target
88 | error = p + up - (w + uw)
89 | #error = p + up * self.production_target - (w + uw * self.production_target)
90 | self.production_target = self.production_controller.update(error)
91 |
92 | self.log('production', {'production': self.production_target,
93 | 'error_cum': self.production_controller.error_cum,
94 | 'error': error})
95 |
96 | self.log('up', {'chosen': up,
97 | 'complex_estimation': self.up_regression.up_delta_price,
98 | 'simple_estimation': self.up_regression.up_price})
99 |
100 | self.log('uw', {'chosen': uw,
101 | 'complex_estimation': self.uw_regression.up_delta_price,
102 | 'simple_estimation': self.uw_regression.up_price})
103 |
--------------------------------------------------------------------------------
/examples/pid_controller/full_pi_controlled/labormarket.py:
--------------------------------------------------------------------------------
1 | """ The market agent
2 | has the demand function q = 102 - p
3 | """
4 | from __future__ import division
5 | import abcEconomics as abce
6 |
7 |
8 | class LaborMarket(abce.Agent, abce.Household, abce.Quote):
9 | def init(self, simulation_parameters, agent_parameters):
10 | self.set_cobb_douglas_utility_function({'cookies': 1})
11 |
12 | def accepting(self):
13 | """ buy a cookies if it is smaller then self.id * 10. create enough
14 | money to by one cookies, if its cheap enough (its a partial equilibrium
15 | model)
16 | """
17 | quote = self.get_quotes('labor')[0]
18 | quantity = max(0, quote.price - 14)
19 | self.create('labor', quantity)
20 | self.accept_quote_partial(quote, quantity)
21 |
22 | def consumption(self):
23 | """ consume the cookie """
24 | self.consume_everything()
25 |
--------------------------------------------------------------------------------
/examples/pid_controller/full_pi_controlled/market.py:
--------------------------------------------------------------------------------
1 | """ The market agent
2 | has the demand function q = 102 - p
3 | """
4 | from __future__ import division
5 | import abcEconomics as abce
6 |
7 |
8 | class Market(abce.Agent, abce.Household):
9 | def init(self, simulation_parameters, agent_parameters):
10 | self.set_cobb_douglas_utility_function({'cookies': 1})
11 |
12 | def buying(self):
13 | """ buy a cookies if it is smaller then self.id * 10. create enough
14 | money to by one cookies, if its cheap enough (its a partial equilibrium
15 | model)
16 | """
17 | offer = self.get_offers('cookies')[0]
18 | quantity = 102 - offer.price
19 | self.message('firm', 0, 'demand', quantity)
20 | if quantity < 0:
21 | quantity = 0
22 | if quantity > offer.quantity:
23 | quantity = offer.quantity
24 | self.create('money', quantity * offer.price - self.possession('money'))
25 | self.accept(offer, quantity)
26 |
27 | def consumption(self):
28 | """ consume the cookie """
29 | self.consume_everything()
30 |
--------------------------------------------------------------------------------
/examples/pid_controller/full_pi_controlled/picontroller.py:
--------------------------------------------------------------------------------
1 | from builtins import object
2 |
3 |
4 | class PiController(object):
5 | """ The PiController, learns from an error to set a certain control.
6 | It only produces positive signals.
7 |
8 | Args:
9 | error_parameter:
10 | weight for error
11 | cumulative_error_parameter:
12 | weight for cumulative error
13 | positive:
14 | if true it truncates negative control signals to 0
15 | """
16 |
17 | def __init__(self, error_parameter, cumulative_error_parameter, output0, positive=False):
18 | self.a = error_parameter
19 | self.b = cumulative_error_parameter
20 | self.error_cum = 0
21 | if positive:
22 | self.update = self.update_positive
23 | else:
24 | self.update = self._update
25 | self.output0 = output0
26 |
27 | def _update(self, error):
28 | """ Add a new error to the learning and return the updated control
29 | variable
30 | """
31 | self.error_cum += error
32 | output = self.a * error + self.b * self.error_cum + self.output0
33 | return output
34 |
35 | def update_positive(self, error):
36 | return max(0, self._update(error))
37 |
--------------------------------------------------------------------------------
/examples/pid_controller/full_pi_controlled/start.py:
--------------------------------------------------------------------------------
1 | """ A simulation of the first Model of Ernesto Carrella's paper:
2 | Sticky Prices Microfoundations in a Agent Based Supply Chain
3 | Section 4 Firms and Production
4 |
5 | Here we have one firm and one market agent. The market agent
6 | has the demand function q = 102 - p
7 |
8 | """
9 | from __future__ import division
10 | from firm import Firm
11 | from market import Market
12 | from labormarket import LaborMarket
13 | from abcEconomics import Simulation
14 |
15 |
16 | def main():
17 | s = Simulation(name='Sticky Prices Microfoundations')
18 | s.declare_perishable('labor')
19 |
20 | firms = s.build_agents(Firm, 'firm', 1)
21 | market = s.build_agents(Market, 'market', 1)
22 | labormarket = s.build_agents(LaborMarket, 'labormarket', 1)
23 | for r in range(20):
24 | s.advance_round(r)
25 | firms.do('quote_hire')
26 | labormarket.do('accepting')
27 | firms.do('hire')
28 | firms.do('my_production')
29 | firms.do('selling')
30 | market.do('buying')
31 | firms.do('adjust_price')
32 | firms.do('adjust_quantity')
33 | market.do('consumption')
34 | # s.finalize()
35 |
36 |
37 | if __name__ == '__main__':
38 | main()
39 |
--------------------------------------------------------------------------------
/examples/pid_controller/full_pi_controlled/upregression.py:
--------------------------------------------------------------------------------
1 | from builtins import object
2 | # pylint: disable=C0103, W0201
3 | from sklearn import linear_model
4 |
5 |
6 | class UPRegression(object):
7 | def __init__(self, memory):
8 | self.memory = memory
9 | self.learn_delta_price = linear_model.LinearRegression()
10 | self.learn_price = linear_model.LinearRegression()
11 | self.dX = []
12 | self.dy = []
13 | self.X = []
14 | self.y = []
15 |
16 | def fit(self, price, price_1, L, L_1):
17 | self.price = price
18 | self.price_1 = price_1
19 | self.L = L
20 | self.L_1 = L_1
21 | self.y.append(price)
22 | self.dy.append(price - price_1)
23 | self.dX.append([1, L - L_1, price_1, L_1])
24 | self.X.append([1, L])
25 | if len(self.y) >= self.memory:
26 | del self.y[0]
27 | del self.dy[0]
28 | del self.X[0]
29 | del self.dX[0]
30 |
31 | self.learn_delta_price.fit(self.dX, self.dy)
32 | self.learn_price.fit(self.X, self.y)
33 |
34 | def predict(self):
35 | B_p = self.learn_delta_price.coef_
36 | error_delta_price = self.learn_delta_price.predict(
37 | [1, self.L - self.L_1, self.price_1, self.L_1]) + self.price_1 - self.price
38 | error_price = self.learn_price.predict([1, self.L]) - self.price
39 | self.up_delta_price = - B_p[3] / B_p[2] * self.L
40 | self.up_price = self.learn_price.coef_[1] * self.L
41 | if error_delta_price ** 2 < error_price ** 2:
42 | up = self.up_delta_price
43 | else:
44 | up = self.up_price
45 | return up
46 |
--------------------------------------------------------------------------------
/examples/pid_controller/simple_seller_example/firm.py:
--------------------------------------------------------------------------------
1 | """ This firm uses a PID controller to set the highest possible price at which
2 | all it's goods can be sold.
3 | """
4 | from __future__ import division
5 | from builtins import range
6 | import abcEconomics as abce
7 | from abcEconomics import NotEnoughGoods
8 |
9 |
10 | class Firm(abce.Agent, abce.Firm, abce.Quote):
11 | def init(self):
12 | self.error_cum = 0
13 | self.price = 20
14 |
15 | def production(self):
16 | """ produce missing cookies """
17 | self.create('cookies', 4 - self.possession('cookies'))
18 |
19 | def quote(self):
20 | """ make a non binding quote at self.price """
21 | for id in range(10):
22 | self.quote_sell(('household', id), 'cookies',
23 | self.possession('cookies'), self.price)
24 |
25 | def selling(self):
26 | """ sell to all agents that accepted the price cookies, if there are
27 | not enough cookies, not all households are served. (ABCE makes sure
28 | that who is served is random)
29 | """
30 | orders = self.get_offers('cookies', descending=True)
31 | for order in orders:
32 | try:
33 | self.accept(order)
34 | except NotEnoughGoods:
35 | break
36 |
37 | total_orders = sum([order.quantity for order in orders])
38 | error = total_orders - 4
39 | self.error_cum += error
40 | self.price = max(0, 0.15 * error + 0.1 * self.error_cum)
41 | self.log('', {'price': self.price,
42 | 'error_cum': self.error_cum, 'error': error})
43 |
--------------------------------------------------------------------------------
/examples/pid_controller/simple_seller_example/household.py:
--------------------------------------------------------------------------------
1 | """ a Household, different Household agents have a different willingness to
2 | pay. The willingness to pay is self.id * 10. Which results in a downward
3 | sloping demand curve
4 | """
5 | from __future__ import division
6 | import abcEconomics as abce
7 |
8 |
9 | class Household(abce.Agent, abce.Household, abce.Quote):
10 | def init(self):
11 | self.set_cobb_douglas_utility_function({'cookies': 1})
12 |
13 | def buying(self):
14 | """ buy a cookies if it is smaller then self.id * 10. create enough
15 | money to by one cookies, if its cheap enough (its a partial equlibirum
16 | model)
17 | """
18 | quotes = self.get_quotes('cookies')
19 | for quote in quotes:
20 | if quote.price <= self.id * 10 and self.possession('cookies') == 0:
21 | self.create('money', quote.price)
22 | self.accept_quote_partial(quote, min(1, quote.quantity))
23 |
24 | def consumption(self):
25 | """ consume the cookie """
26 | self.consume_everything()
27 |
--------------------------------------------------------------------------------
/examples/pid_controller/simple_seller_example/print_profile.py:
--------------------------------------------------------------------------------
1 | import pstats
2 | import sys
3 |
4 | p = pstats.Stats(sys.argv[1])
5 | p.strip_dirs().sort_stats('cumulative').print_stats(30)
6 |
--------------------------------------------------------------------------------
/examples/pid_controller/simple_seller_example/profile.sh:
--------------------------------------------------------------------------------
1 | pyprof2calltree -i profile.prof -o callgrind.output
2 |
3 |
--------------------------------------------------------------------------------
/examples/pid_controller/simple_seller_example/simulation_parameters.csv:
--------------------------------------------------------------------------------
1 | # name random_seed num_rounds trade_logging trade_repetitions
2 | 1 test_sim_1_ None 1000 individual 20
3 |
--------------------------------------------------------------------------------
/examples/pid_controller/simple_seller_example/start.py:
--------------------------------------------------------------------------------
1 | """ A simulation of the first Model of Ernesto Carrella's paper: Zero-Knowledge Traders,
2 | journal of artificial societies and social simulation, december 2013
3 |
4 | This is a partial 'equilibrium' model. A firm has a fixed production of 4 it offers
5 | this to a fixed population of 10 household. The household willingness to pay is
6 | household id * 10 (10, 20, 30 ... 90).
7 | The firms sets the prices using a PID controller.
8 | """
9 | from __future__ import division
10 | from firm import Firm
11 | from household import Household
12 | from abcEconomics import Simulation
13 |
14 |
15 | simulation_parameters = {'random_seed': None,
16 | 'rounds': 300}
17 |
18 |
19 | def main(simulation_parameters):
20 | s = Simulation()
21 |
22 | firms = s.build_agents(Firm, 'firm', 10)
23 | households = s.build_agents(Household, 'household', 10)
24 | for r in range(int(simulation_parameters['rounds'])):
25 | s.advance_round(r)
26 | firms.panel_log(possessions=['cookies'])
27 | firms.quote()
28 | households.buying()
29 | firms.selling()
30 | households.panel_log(possessions=['cookies'])
31 | households.consumption()
32 | s.finalize()
33 |
34 |
35 | if __name__ == '__main__':
36 | main(simulation_parameters)
37 |
--------------------------------------------------------------------------------
/examples/sugarscape/agents.py:
--------------------------------------------------------------------------------
1 | import random
2 | import pylab
3 | from abcEconomics import Agent, NotEnoughGoods
4 |
5 |
6 | def get_distance(pos_1, pos_2):
7 | """
8 | Calculate euclidean distance between two positions.
9 | """
10 | x1, y1 = pos_1
11 | x2, y2 = pos_2
12 | dx = x1 - x2
13 | dy = y1 - y2
14 | return pylab.sqrt(dx ** 2 + dy ** 2)
15 |
16 |
17 | class SugarPatch(Agent):
18 | """
19 | SugarPatch is a FSM that
20 | - contains an amount of sugar
21 | - grows 1 amount of sugar at each turn (Epstein's rule G1).
22 | """
23 | def __init__(self, pos, max_sugar):
24 | self.pos = pos
25 | self.amount = max_sugar
26 | self.max_sugar = max_sugar
27 |
28 | def step(self):
29 | self.amount = min([self.max_sugar, self.amount + 1])
30 |
31 |
32 | class SpicePatch(Agent):
33 | """
34 | SpicePatch is a FSM that
35 | - contains an amount of spice
36 | - grows 1 amount of spice at each turn (Epstein's rule G1).
37 | """
38 | def __init__(self, pos, max_spice):
39 | self.pos = pos
40 | self.amount = max_spice
41 | self.max_spice = max_spice
42 |
43 | def step(self):
44 | self.amount = min([self.max_spice, self.amount + 1])
45 |
46 |
47 | class SsAgent(Agent):
48 | def init(self, parameters, agent_parameters):
49 | self.moore = False
50 | self.grid = parameters["grid"]
51 | self.set_at_random_unoccupied_pos()
52 | self.grid.place_agent(self, self.pos)
53 |
54 | # Each agent is endowed by a random amount of sugar and spice
55 | self.create('sugar', random.randrange(25, 50))
56 | self.create('spice', random.randrange(25, 50))
57 |
58 | # Each agent's phenotype is initialized with random value
59 | self.metabolism = random.randrange(1, 5)
60 | self.metabolism_spice = random.randrange(1, 5)
61 | self.vision = random.randrange(1, 6)
62 |
63 | self.prices = []
64 | self.dead = False
65 |
66 | def set_at_random_unoccupied_pos(self):
67 | x = random.randrange(self.grid.width)
68 | y = random.randrange(self.grid.height)
69 | if not self.is_occupied((x, y)):
70 | self.pos = (x, y)
71 | return
72 | self.set_at_random_unoccupied_pos()
73 |
74 | def get_sugar(self, pos):
75 | this_cell = self.grid.get_cell_list_contents([pos])
76 | for agent in this_cell:
77 | if type(agent) is SugarPatch:
78 | return agent
79 |
80 | def get_spice(self, pos):
81 | this_cell = self.grid.get_cell_list_contents([pos])
82 | for agent in this_cell:
83 | if isinstance(agent, SpicePatch):
84 | return agent
85 |
86 | def get_ssagent(self, pos):
87 | this_cell = self.grid.get_cell_list_contents([pos])
88 | for agent in this_cell:
89 | if isinstance(agent, SsAgent):
90 | return agent
91 |
92 | def is_occupied(self, pos):
93 | this_cell = self.grid.get_cell_list_contents([pos])
94 | return len(this_cell) > 2
95 |
96 | def move(self):
97 | if self.dead:
98 | return
99 |
100 | # Epstein rule M
101 |
102 | # 1. Get neighborhood within vision.
103 | neighbors = [i for i in self.grid.get_neighborhood(self.pos, self.moore,
104 | False, radius=self.vision) if not self.is_occupied(i)]
105 | neighbors.append(self.pos)
106 | eps = 0.0000001
107 |
108 | # 2. Find the patch which produces maximum welfare.
109 | welfares = [self.welfare(self['sugar'] + self.get_sugar(pos).amount,
110 | self['spice'] + self.get_spice(pos).amount) for pos in neighbors]
111 | max_welfare = max(welfares)
112 | candidate_indices = [i for i in range(len(welfares)) if abs(welfares[i] -
113 | max_welfare) < eps]
114 | candidates = [neighbors[i] for i in candidate_indices]
115 |
116 | # 3. Find the nearest patch among the candidate.
117 | try:
118 | min_dist = min([get_distance(self.pos, pos) for pos in candidates])
119 | except Exception:
120 | print(welfares)
121 | print(self.welfare())
122 | print(self['sugar'])
123 | print(self['spice'])
124 | print(neighbors)
125 | exit()
126 | final_candidates = [pos for pos in candidates if abs(get_distance(self.pos,
127 | pos) - min_dist) < eps]
128 | random.shuffle(final_candidates)
129 |
130 | # 4. Move agent.
131 | self.grid.move_agent(self, final_candidates[0])
132 |
133 | def eat(self):
134 | if self.dead:
135 | return
136 |
137 | # Fetch sugar and spice patch
138 | sugar_patch = self.get_sugar(self.pos)
139 | spice_patch = self.get_spice(self.pos)
140 |
141 | # Harvest sugar and spice
142 | self.create('sugar', sugar_patch.amount)
143 | self.create('spice', spice_patch.amount)
144 | sugar_patch.amount = 0
145 | spice_patch.amount = 0
146 |
147 | # Metabolize
148 | try:
149 | self.destroy('sugar', self.metabolism)
150 | self.destroy('spice', self.metabolism_spice)
151 | except NotEnoughGoods:
152 | self.delete_agent('SsAgent', self.id, quite=False)
153 | self.grid.remove_agent(self)
154 | self.dead = True
155 |
156 | def sell_spice(self, other):
157 | mrs_self = self._calculate_MRS()
158 | mrs_other = other._calculate_MRS()
159 |
160 | price = pylab.sqrt(mrs_self * mrs_other)
161 | price = max(0.000000000001, price)
162 | if self['spice'] >= 1:
163 | self.sell(other.name, 'spice', quantity=1, price=price, currency='sugar')
164 | if self['sugar'] >= 1:
165 | quantity = min(1, self['sugar'] * price)
166 | self.buy(other.name, 'spice', quantity=quantity, price=1 / price, currency='sugar')
167 |
168 | def trade_with_neighbors(self):
169 | if self.dead:
170 | return
171 | # von Neumann neighbors
172 | neighbor_agents = [self.get_ssagent(pos) for pos in self.grid.get_neighborhood(self.pos, self.moore,
173 | False, radius=self.vision) if self.is_occupied(pos)]
174 | if neighbor_agents:
175 | random.shuffle(neighbor_agents)
176 | count = 0
177 | for a in neighbor_agents:
178 | if a:
179 | self.sell_spice(a)
180 | count += 1
181 | if count > 0:
182 | prices = [p for p in self.prices if p]
183 | self.prices = []
184 | # print("%d Traded with %d out of %d neighbors" % (self.id, count, len(neighbor_agents)))
185 | return prices
186 | return []
187 |
188 | def trade(self):
189 | # Epstein rule T for a pair of agents, page 105
190 | for offer in self.get_offers('spice'):
191 | baseline = self.welfare()
192 | if offer.buysell == 115:
193 | welfare = self.welfare(spice=self['spice'] + 1, sugar=self['sugar'] - 1)
194 | elif offer.buysell == 98:
195 | welfare = self.welfare(spice=self['spice'] - 1, sugar=self['sugar'] + 1)
196 | if welfare > baseline:
197 | try:
198 | self.accept(offer)
199 | except (NotEnoughGoods, KeyError):
200 | self.reject(offer)
201 |
202 | def welfare(self, sugar=None, spice=None):
203 | if sugar is None:
204 | sugar = self['sugar']
205 | if spice is None:
206 | spice = self['spice']
207 | m_total = self.metabolism + self.metabolism_spice
208 | return sugar ** (self.metabolism / m_total) * spice ** (self.metabolism_spice / m_total)
209 |
210 | def _calculate_MRS(self):
211 | return (self['spice'] / self.metabolism_spice) / (self['sugar'] / self.metabolism)
212 |
213 | def _compare_MRS(self, agent):
214 | return self._calculate_MRS() == agent._calculate_MRS()
215 |
--------------------------------------------------------------------------------
/examples/sugarscape/start.py:
--------------------------------------------------------------------------------
1 | """
2 | Sugarscape ({G1}, {M, T}) -- Epstein Chapter 4
3 | Heavily use Mesa for space.
4 | """
5 |
6 | import matplotlib
7 | matplotlib.use('TkAgg')
8 |
9 | from agents import SugarPatch, SpicePatch, SsAgent
10 | from abcEconomics import Simulation
11 | from mesa.space import MultiGrid
12 | import pylab
13 |
14 |
15 | def main():
16 | s = Simulation(processes=1)
17 | grid = MultiGrid(50, 50, True)
18 |
19 | # build sugar and spice
20 | sugar_distribution = pylab.genfromtxt("sugar-map.txt")
21 | spice_distribution = sugar_distribution.T
22 | sugars = []
23 | spices = []
24 | for _, x, y in grid.coord_iter():
25 | max_sugar = sugar_distribution[x, y]
26 | max_spice = spice_distribution[x, y]
27 | sugar = SugarPatch((x, y), max_sugar)
28 | spice = SpicePatch((x, y), max_spice)
29 | sugars.append(sugar)
30 | spices.append(spice)
31 | grid.place_agent(sugar, (x, y))
32 | grid.place_agent(spice, (x, y))
33 |
34 | # build agents
35 | agents = s.build_agents(SsAgent, 'SsAgent', 100,
36 | parameters={'grid': grid})
37 |
38 | # prices = []
39 | for r in range(100):
40 | s.advance_round(r)
41 | for sugar in sugars:
42 | sugar.step()
43 | for spice in spices:
44 | spice.step()
45 | agents.move()
46 | agents.eat()
47 | print(',', len(agents.trade_with_neighbors()))
48 |
49 | agents.trade()
50 | agents.agg_log(possessions=['sugar', 'spice'])
51 |
52 | s.finalize()
53 |
54 |
55 | if __name__ == '__main__':
56 | main()
57 |
--------------------------------------------------------------------------------
/examples/sugarscape/sugar-map.txt:
--------------------------------------------------------------------------------
1 | 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 2 2 2 2 2 2 2 2
2 | 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 2 2 2 2 2 2
3 | 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 2 2 2 2
4 | 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 2 2 2
5 | 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 2 2
6 | 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 2 2 2 2 2 2 3 3 3 3 3 3 3 4 4 4 4 3 3 3 3 3 3 3 2 2
7 | 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 2 2 2 2 2 3 3 3 3 3 3 4 4 4 4 4 4 4 4 3 3 3 3 3 3 2
8 | 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 2 2 2 2 2 2 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 3 3 3 3 3 2
9 | 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 2 2 2 2 2 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 3 3 3 3 3 3
10 | 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 3 3 3 3 3
11 | 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 3 3 3 3 3
12 | 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 3 3 3 3 3
13 | 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 3 3 3 3 3
14 | 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 3 3 3 3 3 3
15 | 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 3 3 3 3 3 2
16 | 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4 3 3 3 3 3 3 2
17 | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 4 4 4 4 3 3 3 3 3 3 3 2 2
18 | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 2 2
19 | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 2 2 2
20 | 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 2 2 2 2
21 | 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 2 2 2 2 2 2
22 | 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 2 2 2 2 2 2 2 2
23 | 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1
24 | 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1
25 | 1 1 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 1
26 | 1 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1
27 | 1 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1
28 | 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1
29 | 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 2 2 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1
30 | 2 2 2 2 2 2 3 3 3 3 3 3 3 4 4 4 4 3 3 3 3 3 3 3 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1
31 | 2 2 2 2 2 3 3 3 3 3 3 4 4 4 4 4 4 4 4 3 3 3 3 3 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 0 0 0
32 | 2 2 2 2 2 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 3 3 3 3 3 3 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0
33 | 2 2 2 2 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 3 3 3 3 3 3 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0
34 | 2 2 2 2 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 3 3 3 3 3 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0
35 | 2 2 2 2 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 3 3 3 3 3 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0
36 | 2 2 2 2 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 3 3 3 3 3 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0
37 | 2 2 2 2 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 3 3 3 3 3 2 2 2 2 2 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0
38 | 2 2 2 2 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 3 3 3 3 3 3 2 2 2 2 2 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0
39 | 2 2 2 2 2 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 3 3 3 3 3 2 2 2 2 2 2 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0
40 | 2 2 2 2 2 3 3 3 3 3 3 4 4 4 4 4 4 4 4 3 3 3 3 3 3 2 2 2 2 2 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0
41 | 2 2 2 2 2 2 3 3 3 3 3 3 3 4 4 4 4 3 3 3 3 3 3 3 2 2 2 2 2 2 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0
42 | 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 2 2 2 2 2 2 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0
43 | 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 2 2 2 2 2 2 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0
44 | 1 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0
45 | 1 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0
46 | 1 1 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0
47 | 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0
48 | 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0
49 | 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0
50 | 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
51 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | git+https://github.com/AB-CE/abce.git@26c1b4eb0526e1ad569246d7f3cf9130dcd13a29#egg=abce
2 |
--------------------------------------------------------------------------------