├── .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 | --------------------------------------------------------------------------------