From f177f8f0ba090e9a2d7e571b36749a0908564784 Mon Sep 17 00:00:00 2001 From: Manuel Plonski Date: Thu, 12 Jan 2023 16:41:38 +0100 Subject: [PATCH] adding ai_economist for modding --- .gitignore | 4 +- agents/consumer_agent.py | 11 + agents/trading_agent.py | 11 + ai_economist/LICENSE.txt | 12 + ai_economist/__init__.py | 7 + ai_economist/datasets/__init__.py | 5 + .../datasets/covid19_datasets/README.md | 27 + .../datasets/covid19_datasets/__init__.py | 5 + .../data_and_fitted_params/fitted_params.json | 1 + .../model_constants.json | 1 + .../real_world_data.npz | Bin 0 -> 2120354 bytes .../fit_model_parameters.ipynb | 1450 +++++++++++++ .../gather_real_world_data.ipynb | 846 ++++++++ .../datasets/covid19_datasets/us_deaths.py | 54 + .../datasets/covid19_datasets/us_policies.py | 122 ++ .../covid19_datasets/us_unemployment.py | 128 ++ .../covid19_datasets/us_vaccinations.py | 61 + ai_economist/foundation/__init__.py | 18 + ai_economist/foundation/agents/__init__.py | 12 + ai_economist/foundation/agents/mobiles.py | 18 + ai_economist/foundation/agents/planners.py | 40 + ai_economist/foundation/base/__init__.py | 5 + ai_economist/foundation/base/base_agent.py | 490 +++++ .../foundation/base/base_component.py | 406 ++++ ai_economist/foundation/base/base_env.py | 1157 ++++++++++ ai_economist/foundation/base/registrar.py | 103 + ai_economist/foundation/base/world.py | 495 +++++ .../foundation/components/__init__.py | 19 + ai_economist/foundation/components/build.py | 266 +++ .../components/continuous_double_auction.py | 679 ++++++ .../components/covid19_components.py | 663 ++++++ .../components/covid19_components_step.cu | 263 +++ ai_economist/foundation/components/move.py | 222 ++ .../foundation/components/redistribution.py | 1202 ++++++++++ .../foundation/components/simple_labor.py | 134 ++ ai_economist/foundation/components/utils.py | 115 + ai_economist/foundation/entities/__init__.py | 9 + .../foundation/entities/endogenous.py | 36 + ai_economist/foundation/entities/landmarks.py | 88 + ai_economist/foundation/entities/resources.py | 84 + ai_economist/foundation/env_wrapper.py | 418 ++++ ai_economist/foundation/scenarios/__init__.py | 14 + .../foundation/scenarios/covid19/__init__.py | 5 + .../scenarios/covid19/covid19_build.cu | 13 + .../scenarios/covid19/covid19_env.py | 1687 ++++++++++++++ .../scenarios/covid19/covid19_env_step.cu | 620 ++++++ .../key_to_check_activation_code_against | 27 + .../scenarios/one_step_economy/__init__.py | 5 + .../one_step_economy/one_step_economy.py | 336 +++ .../simple_wood_and_stone/__init__.py | 5 + .../simple_wood_and_stone/dynamic_layout.py | 1021 +++++++++ .../simple_wood_and_stone/layout_from_file.py | 800 +++++++ .../closed_quadrant_25x25_20each_30clump.txt | 1 + .../map_txt/env-pure_and_mixed-15x15.txt | 1 + .../map_txt/env-pure_and_mixed-25x25.txt | 1 + .../map_txt/env-pure_and_mixed-40x40.txt | 1 + .../map_txt/quadrant_25x25_20each_30clump.txt | 1 + ...quadrant_25x25_20each_30clump_no_water.txt | 1 + .../map_txt/quadrant_40x40_50each.txt | 1 + .../quadrant_40x40_50each_no_water.txt | 1 + .../map_txt/quadrant_8x8_4each_8clump.txt | 1 + .../map_txt/top_wood_bottom_stone_14x14.txt | 1 + .../map_txt/uniform_25x25_25each_65clump.txt | 1 + .../foundation/scenarios/utils/__init__.py | 5 + .../foundation/scenarios/utils/rewards.py | 133 ++ .../scenarios/utils/social_metrics.py | 75 + ai_economist/foundation/utils.py | 123 ++ ai_economist/real_business_cycle/README.md | 120 + .../real_business_cycle/experiment_utils.py | 242 +++ .../real_business_cycle/rbc/__init__.py | 5 + .../real_business_cycle/rbc/constants.py | 638 ++++++ .../real_business_cycle/rbc/cuda/firm_rbc.cu | 912 ++++++++ .../real_business_cycle/rbc/cuda_manager.py | 1930 +++++++++++++++++ .../real_business_cycle/rbc/networks.py | 114 + ai_economist/real_business_cycle/rbc/util.py | 110 + .../real_business_cycle/train_bestresponse.py | 108 + .../real_business_cycle/train_multi_exps.py | 119 + .../real_business_cycle/train_single_exp.py | 51 + ai_economist/training/__init__.py | 5 + .../covid_and_economy_environment.yaml | 77 + ai_economist/training/training_script.py | 134 ++ envs/__init__.py | 5 + envs/base_econ_wrapper.py | 169 ++ envs/reciever_econ_wrapper.py | 67 + reqirements.txt | 2 +- 85 files changed, 19373 insertions(+), 2 deletions(-) create mode 100644 agents/consumer_agent.py create mode 100644 agents/trading_agent.py create mode 100644 ai_economist/LICENSE.txt create mode 100644 ai_economist/__init__.py create mode 100644 ai_economist/datasets/__init__.py create mode 100644 ai_economist/datasets/covid19_datasets/README.md create mode 100644 ai_economist/datasets/covid19_datasets/__init__.py create mode 100644 ai_economist/datasets/covid19_datasets/data_and_fitted_params/fitted_params.json create mode 100644 ai_economist/datasets/covid19_datasets/data_and_fitted_params/model_constants.json create mode 100644 ai_economist/datasets/covid19_datasets/data_and_fitted_params/real_world_data.npz create mode 100644 ai_economist/datasets/covid19_datasets/fit_model_parameters.ipynb create mode 100644 ai_economist/datasets/covid19_datasets/gather_real_world_data.ipynb create mode 100644 ai_economist/datasets/covid19_datasets/us_deaths.py create mode 100644 ai_economist/datasets/covid19_datasets/us_policies.py create mode 100644 ai_economist/datasets/covid19_datasets/us_unemployment.py create mode 100644 ai_economist/datasets/covid19_datasets/us_vaccinations.py create mode 100644 ai_economist/foundation/__init__.py create mode 100644 ai_economist/foundation/agents/__init__.py create mode 100644 ai_economist/foundation/agents/mobiles.py create mode 100644 ai_economist/foundation/agents/planners.py create mode 100644 ai_economist/foundation/base/__init__.py create mode 100644 ai_economist/foundation/base/base_agent.py create mode 100644 ai_economist/foundation/base/base_component.py create mode 100644 ai_economist/foundation/base/base_env.py create mode 100644 ai_economist/foundation/base/registrar.py create mode 100644 ai_economist/foundation/base/world.py create mode 100644 ai_economist/foundation/components/__init__.py create mode 100644 ai_economist/foundation/components/build.py create mode 100644 ai_economist/foundation/components/continuous_double_auction.py create mode 100644 ai_economist/foundation/components/covid19_components.py create mode 100644 ai_economist/foundation/components/covid19_components_step.cu create mode 100644 ai_economist/foundation/components/move.py create mode 100644 ai_economist/foundation/components/redistribution.py create mode 100644 ai_economist/foundation/components/simple_labor.py create mode 100644 ai_economist/foundation/components/utils.py create mode 100644 ai_economist/foundation/entities/__init__.py create mode 100644 ai_economist/foundation/entities/endogenous.py create mode 100644 ai_economist/foundation/entities/landmarks.py create mode 100644 ai_economist/foundation/entities/resources.py create mode 100644 ai_economist/foundation/env_wrapper.py create mode 100644 ai_economist/foundation/scenarios/__init__.py create mode 100644 ai_economist/foundation/scenarios/covid19/__init__.py create mode 100644 ai_economist/foundation/scenarios/covid19/covid19_build.cu create mode 100644 ai_economist/foundation/scenarios/covid19/covid19_env.py create mode 100644 ai_economist/foundation/scenarios/covid19/covid19_env_step.cu create mode 100644 ai_economist/foundation/scenarios/covid19/key_to_check_activation_code_against create mode 100644 ai_economist/foundation/scenarios/one_step_economy/__init__.py create mode 100644 ai_economist/foundation/scenarios/one_step_economy/one_step_economy.py create mode 100644 ai_economist/foundation/scenarios/simple_wood_and_stone/__init__.py create mode 100644 ai_economist/foundation/scenarios/simple_wood_and_stone/dynamic_layout.py create mode 100644 ai_economist/foundation/scenarios/simple_wood_and_stone/layout_from_file.py create mode 100644 ai_economist/foundation/scenarios/simple_wood_and_stone/map_txt/closed_quadrant_25x25_20each_30clump.txt create mode 100644 ai_economist/foundation/scenarios/simple_wood_and_stone/map_txt/env-pure_and_mixed-15x15.txt create mode 100644 ai_economist/foundation/scenarios/simple_wood_and_stone/map_txt/env-pure_and_mixed-25x25.txt create mode 100644 ai_economist/foundation/scenarios/simple_wood_and_stone/map_txt/env-pure_and_mixed-40x40.txt create mode 100644 ai_economist/foundation/scenarios/simple_wood_and_stone/map_txt/quadrant_25x25_20each_30clump.txt create mode 100644 ai_economist/foundation/scenarios/simple_wood_and_stone/map_txt/quadrant_25x25_20each_30clump_no_water.txt create mode 100644 ai_economist/foundation/scenarios/simple_wood_and_stone/map_txt/quadrant_40x40_50each.txt create mode 100644 ai_economist/foundation/scenarios/simple_wood_and_stone/map_txt/quadrant_40x40_50each_no_water.txt create mode 100644 ai_economist/foundation/scenarios/simple_wood_and_stone/map_txt/quadrant_8x8_4each_8clump.txt create mode 100644 ai_economist/foundation/scenarios/simple_wood_and_stone/map_txt/top_wood_bottom_stone_14x14.txt create mode 100644 ai_economist/foundation/scenarios/simple_wood_and_stone/map_txt/uniform_25x25_25each_65clump.txt create mode 100644 ai_economist/foundation/scenarios/utils/__init__.py create mode 100644 ai_economist/foundation/scenarios/utils/rewards.py create mode 100644 ai_economist/foundation/scenarios/utils/social_metrics.py create mode 100644 ai_economist/foundation/utils.py create mode 100644 ai_economist/real_business_cycle/README.md create mode 100644 ai_economist/real_business_cycle/experiment_utils.py create mode 100644 ai_economist/real_business_cycle/rbc/__init__.py create mode 100644 ai_economist/real_business_cycle/rbc/constants.py create mode 100644 ai_economist/real_business_cycle/rbc/cuda/firm_rbc.cu create mode 100644 ai_economist/real_business_cycle/rbc/cuda_manager.py create mode 100644 ai_economist/real_business_cycle/rbc/networks.py create mode 100644 ai_economist/real_business_cycle/rbc/util.py create mode 100644 ai_economist/real_business_cycle/train_bestresponse.py create mode 100644 ai_economist/real_business_cycle/train_multi_exps.py create mode 100644 ai_economist/real_business_cycle/train_single_exp.py create mode 100644 ai_economist/training/__init__.py create mode 100644 ai_economist/training/run_configs/covid_and_economy_environment.yaml create mode 100644 ai_economist/training/training_script.py create mode 100644 envs/__init__.py create mode 100644 envs/base_econ_wrapper.py create mode 100644 envs/reciever_econ_wrapper.py diff --git a/.gitignore b/.gitignore index 2a24376..ee66eb6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ logs/* -_pycache_ \ No newline at end of file +_pycache_ +*.pyc +*tfevents* \ No newline at end of file diff --git a/agents/consumer_agent.py b/agents/consumer_agent.py new file mode 100644 index 0000000..36c6999 --- /dev/null +++ b/agents/consumer_agent.py @@ -0,0 +1,11 @@ +from ai_economist.foundation.base.base_agent import BaseAgent, agent_registry + + +@agent_registry.add +class ConsumerAgent(BaseAgent): + """ + A basic mobile agent represents an individual actor in the economic simulation. + "Mobile" refers to agents of this type being able to move around in the 2D world. + """ + + name = "ConsumerAgent" \ No newline at end of file diff --git a/agents/trading_agent.py b/agents/trading_agent.py new file mode 100644 index 0000000..10a8245 --- /dev/null +++ b/agents/trading_agent.py @@ -0,0 +1,11 @@ +from ai_economist.foundation.base.base_agent import BaseAgent, agent_registry + + +@agent_registry.add +class TradingAgent(BaseAgent): + """ + A basic mobile agent represents an individual actor in the economic simulation. + "Mobile" refers to agents of this type being able to move around in the 2D world. + """ + + name = "TradingAgent" \ No newline at end of file diff --git a/ai_economist/LICENSE.txt b/ai_economist/LICENSE.txt new file mode 100644 index 0000000..53866a3 --- /dev/null +++ b/ai_economist/LICENSE.txt @@ -0,0 +1,12 @@ +Copyright (c) 2020, Salesforce.com, Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +* Neither the name of Salesforce.com nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/ai_economist/__init__.py b/ai_economist/__init__.py new file mode 100644 index 0000000..5dd0f79 --- /dev/null +++ b/ai_economist/__init__.py @@ -0,0 +1,7 @@ +# Copyright (c) 2020, salesforce.com, inc. +# All rights reserved. +# SPDX-License-Identifier: BSD-3-Clause +# For full license text, see the LICENSE file in the repo root +# or https://opensource.org/licenses/BSD-3-Clause + +from ai_economist import foundation diff --git a/ai_economist/datasets/__init__.py b/ai_economist/datasets/__init__.py new file mode 100644 index 0000000..93bee4b --- /dev/null +++ b/ai_economist/datasets/__init__.py @@ -0,0 +1,5 @@ +# Copyright (c) 2021, salesforce.com, inc. +# All rights reserved. +# SPDX-License-Identifier: BSD-3-Clause +# For full license text, see the LICENSE file in the repo root +# or https://opensource.org/licenses/BSD-3-Clause diff --git a/ai_economist/datasets/covid19_datasets/README.md b/ai_economist/datasets/covid19_datasets/README.md new file mode 100644 index 0000000..a862ebf --- /dev/null +++ b/ai_economist/datasets/covid19_datasets/README.md @@ -0,0 +1,27 @@ +## List of COVID-19 datasources used + +1. **US state government policies** (Oxford Covid-19 Government Response Tracker (OxCGRT)) + + https://github.com/OxCGRT/USA-covid-policy + + +2. **US federal government direct payments** (Committee for a Responsible Federal Budget) + + https://www.covidmoneytracker.org/ + + https://docs.google.com/spreadsheets/d/1Nr_J5wLfUT4IzqSXkYbdOXrRgEkBxhX0/edit#gid=682404301 + + +3. **US deaths data** (COVID-19 Data Repository by the Center for Systems Science and Engineering (CSSE) at Johns Hopkins University) + + https://github.com/CSSEGISandData/COVID-19 + + +4. **US unemployment** (Bureau of Labor and Statistics) + + https://www.bls.gov/lau/ + + +5. **US vaccinations** (Our World in Data) + + https://ourworldindata.org/covid-vaccinations \ No newline at end of file diff --git a/ai_economist/datasets/covid19_datasets/__init__.py b/ai_economist/datasets/covid19_datasets/__init__.py new file mode 100644 index 0000000..93bee4b --- /dev/null +++ b/ai_economist/datasets/covid19_datasets/__init__.py @@ -0,0 +1,5 @@ +# Copyright (c) 2021, salesforce.com, inc. +# All rights reserved. +# SPDX-License-Identifier: BSD-3-Clause +# For full license text, see the LICENSE file in the repo root +# or https://opensource.org/licenses/BSD-3-Clause diff --git a/ai_economist/datasets/covid19_datasets/data_and_fitted_params/fitted_params.json b/ai_economist/datasets/covid19_datasets/data_and_fitted_params/fitted_params.json new file mode 100644 index 0000000..0e43bbe --- /dev/null +++ b/ai_economist/datasets/covid19_datasets/data_and_fitted_params/fitted_params.json @@ -0,0 +1 @@ +{"settings": {"LAST_DATE_IN_TRAIN_SET": "2020-11-30", "LAST_DATE_IN_VAL_SET": "2020-12-31", "FILTER_SIZE_UNEMPLOYMENT": 600, "SIMILARITY_REGULARIZATION_UNEMPLOYMENT": 0.5, "SIMILARITY_REGULARIZATION_SIR": 1.0, "env": {"economic_reward_crra_eta": 2, "start_date": "2020-03-22", "infection_too_sick_to_work_rate": 0.1, "pop_between_age_18_65": 0.6, "risk_free_interest_rate": 0.03}}, "BETA_DELAY": 29, "BETA_SLOPES": [-0.005233703270905861, -0.0025096633588151916, -0.009978235905460415, -0.0030498590463604695, -0.009739434907947386, -0.016118405619767906, -0.02216394155353909, -0.01501269383871521, -0.01772321933003828, -0.0035091211514514035, -0.00842426615392502, -0.006421715854186978, -0.007605442605186503, -0.013145828895296467, -0.013021772081998916, -0.011242043826411524, -0.009583325687728843, -0.007332846816941151, -0.012677925062908617, -0.010726278623401973, -0.01320149654376923, -0.020049202898711607, -0.020079903692931216, -0.012961592439659947, -0.009660688484644651, -0.01197325796370973, -0.0031953183140946543, -0.011219460393603341, -0.01161877928285559, -0.010572606153577992, -0.021402323448902912, -0.011571328478690551, -0.015057575233879374, -0.011653349301328355, -0.01420212156352489, -0.015437622231824741, -0.01247166799443816, -0.009058745964208309, -0.016065672658303675, -0.015379928352481361, -0.004690098051335516, -0.0027855147080954887, -0.0017843204990313376, -0.006520659060682759, -0.008618617017313706, -0.011942638319771232, -0.009076932750326299, -0.005580847854404909, -0.01311747891796651, -0.008677318047675145, -0.009906449614086134], "BETA_INTERCEPTS": [0.10550336869678541, 0.09352399879913631, 0.13367068043188485, 0.10161651854073984, 0.1457722708748621, 0.16887787187410597, 0.21729992109816224, 0.17450455331397685, 0.18931639275182566, 0.09984961233900469, 0.12133822448461924, 0.12722467531413584, 0.12175264651385272, 0.1552042495807431, 0.145615506170138, 0.1258875328197989, 0.13389493026715132, 0.1254821302501782, 0.14642572151467384, 0.1563475618087581, 0.16032143059458762, 0.19910298878230218, 0.18446412122461148, 0.15385189814506917, 0.1302208274241928, 0.13736418751161014, 0.1033837852314781, 0.13692185665344278, 0.14309430680693005, 0.13749187639644195, 0.19973224088216823, 0.17203054106935328, 0.18082127751622276, 0.15117289048468205, 0.14160189224730083, 0.17488934332776923, 0.1338827960870001, 0.13066194254313895, 0.17067714865126077, 0.18532521741844085, 0.10703810005198379, 0.09841913100945021, 0.09289613699392442, 0.11940190498430303, 0.12161571930284992, 0.1285837104949546, 0.12668802839364063, 0.10747537477804284, 0.1625164048222745, 0.12008200309258711, 0.13872464377603697], "POLICY_START_DATE": "2020-01-01", "FILTER_LEN": 600, "CONV_LAMBDAS": [27.72427749633789, 62.91925048828125, 129.2318572998047, 264.43182373046875, 542.06005859375], "UNEMPLOYMENT_BIAS": [2.1136703491210938, 4.048089027404785, 4.277402877807617, 3.6710152626037598, 4.371125221252441, 3.5259063243865967, 3.423633575439453, 3.4609005451202393, 4.390212535858154, 3.9631943702697754, 2.5867717266082764, 3.5042591094970703, 2.3830432891845703, 4.411620140075684, 2.3750250339508057, 2.5980374813079834, 2.2534663677215576, 2.4830000400543213, 4.428332805633545, 2.4989376068115234, 3.071929931640625, 1.8633426427841187, 4.299410820007324, 3.195344924926758, 4.691559791564941, 2.994248628616333, 3.1809568405151367, 2.121537923812866, 5.0345377922058105, 2.9758565425872803, 4.2665019035339355, 4.322226047515869, 3.248310089111328, 3.0009894371032715, 1.353360652923584, 3.418184280395508, 2.7211599349975586, 2.9320943355560303, 3.8583884239196777, 3.613877773284912, 1.6023359298706055, 2.3257055282592773, 4.022616863250732, 3.996405601501465, 1.5916776657104492, 2.3988192081451416, 3.0608890056610107, 4.039086818695068, 3.984750747680664, 2.440520763397217, 3.8146729469299316], "GROUPED_CONVOLUTIONAL_FILTER_WEIGHTS": [[[-0.8623685240745544]], [[-0.45006391406059265]], [[-0.00237645348533988]], [[0.8720516562461853]], [[1.3083926439285278]], [[-1.8319092988967896]], [[-0.0032714344561100006]], [[0.017020821571350098]], [[0.9376689195632935]], [[0.7846916913986206]], [[-0.634898841381073]], [[-0.39103013277053833]], [[0.765248715877533]], [[0.5142654180526733]], [[0.8652791380882263]], [[-0.9020224809646606]], [[0.2602136433124542]], [[0.2674458622932434]], [[0.6922929883003235]], [[0.28152236342430115]], [[-1.3684333562850952]], [[-1.136441707611084]], [[0.7635596990585327]], [[0.6395389437675476]], [[1.2692675590515137]], [[-1.3872078657150269]], [[-0.18349117040634155]], [[0.731469988822937]], [[0.4884212017059326]], [[0.7628554105758667]], [[-1.9923702478408813]], [[-0.7254942059516907]], [[0.6672083139419556]], [[0.7401175498962402]], [[0.8266059756278992]], [[-2.0412659645080566]], [[0.2596452832221985]], [[0.7648528814315796]], [[0.628707230091095]], [[0.564267098903656]], [[-0.12891456484794617]], [[-0.3544159233570099]], [[-0.3368719518184662]], [[0.37673136591911316]], [[0.9932129383087158]], [[-1.196645975112915]], [[0.019427649676799774]], [[-0.032719578593969345]], [[1.1447675228118896]], [[0.6738105416297913]], [[-1.0570842027664185]], [[0.19370649755001068]], [[0.6044667363166809]], [[0.5649805068969727]], [[0.7912497520446777]], [[-1.6130324602127075]], [[0.14945188164710999]], [[1.2974058389663696]], [[0.27230948209762573]], [[1.4573169946670532]], [[-1.1152888536453247]], [[-0.1312718242406845]], [[0.4778050482273102]], [[0.8300752639770508]], [[0.3758259117603302]], [[-1.8520358800888062]], [[-1.14521324634552]], [[1.0409947633743286]], [[1.6186996698379517]], [[0.4937899112701416]], [[-1.8565905094146729]], [[-0.5699316263198853]], [[1.2526781558990479]], [[1.0222679376602173]], [[0.8439081907272339]], [[-1.1728674173355103]], [[-0.4200057089328766]], [[0.24613676965236664]], [[0.9689220190048218]], [[0.8804899454116821]], [[-0.6283168792724609]], [[0.018899045884609222]], [[0.4649633467197418]], [[0.5606456995010376]], [[0.7042286992073059]], [[-0.969465970993042]], [[0.7879332304000854]], [[0.4766736328601837]], [[0.38358408212661743]], [[0.5555617809295654]], [[-1.3140840530395508]], [[-0.06866976618766785]], [[0.20318199694156647]], [[1.156029224395752]], [[0.4719393253326416]], [[-0.8908430337905884]], [[0.4366728663444519]], [[-0.03466540947556496]], [[0.5951891541481018]], [[0.26352477073669434]], [[-0.4593994617462158]], [[-0.3329457938671112]], [[-0.4925514757633209]], [[-0.0024775229394435883]], [[1.483317255973816]], [[-2.580217123031616]], [[-0.21124900877475739]], [[1.3222242593765259]], [[0.9738183617591858]], [[1.1125288009643555]], [[-1.5558487176895142]], [[-0.21986360847949982]], [[1.4449234008789062]], [[1.1203645467758179]], [[0.8254041075706482]], [[-1.3072439432144165]], [[-0.3959537446498871]], [[0.5149790048599243]], [[0.5998527407646179]], [[0.6167992949485779]], [[-0.03933652117848396]], [[-0.24180862307548523]], [[0.4485468864440918]], [[1.2529921531677246]], [[0.16725485026836395]], [[-1.1775641441345215]], [[0.210318922996521]], [[0.6509057879447937]], [[0.22740843892097473]], [[0.9219421744346619]], [[-1.1539403200149536]], [[0.07256040722131729]], [[0.6274937391281128]], [[0.7214186191558838]], [[0.2535996437072754]], [[-1.1221498250961304]], [[0.03368315473198891]], [[0.4749435782432556]], [[0.4006294906139374]], [[0.35465008020401]], [[-0.7560990452766418]], [[0.8384001851081848]], [[0.7217393517494202]], [[0.9829040765762329]], [[1.6163955926895142]], [[-1.3966710567474365]], [[-0.06901184469461441]], [[0.9089240431785583]], [[0.7274520397186279]], [[0.6534935235977173]], [[-2.2978711128234863]], [[-0.45931634306907654]], [[0.682558536529541]], [[1.9217747449874878]], [[0.46477916836738586]], [[-0.9368125796318054]], [[-0.7055768370628357]], [[0.45245739817619324]], [[1.149850845336914]], [[0.08268105238676071]], [[-1.498590111732483]], [[-1.053027629852295]], [[1.1536319255828857]], [[0.36754435300827026]], [[1.370290756225586]], [[-1.5895041227340698]], [[-0.022197451442480087]], [[1.3241043090820312]], [[0.33694934844970703]], [[0.6450698971748352]], [[-1.221463680267334]], [[-0.8520716428756714]], [[0.31779128313064575]], [[1.012588381767273]], [[1.3222732543945312]], [[-1.7537246942520142]], [[-0.3384951949119568]], [[1.3620738983154297]], [[0.1112571433186531]], [[1.0902765989303589]], [[-1.899932861328125]], [[0.19727519154548645]], [[0.09953983128070831]], [[0.922265887260437]], [[1.0663707256317139]], [[-1.2837663888931274]], [[-0.7540831565856934]], [[0.7365661859512329]], [[1.1298480033874512]], [[0.6919987797737122]], [[-1.5676841735839844]], [[-0.3267393708229065]], [[0.3632243871688843]], [[0.41000232100486755]], [[1.6217762231826782]], [[-1.136587142944336]], [[-0.27317526936531067]], [[0.514305830001831]], [[0.01356478687375784]], [[1.5590686798095703]], [[-1.5125669240951538]], [[-0.6680471897125244]], [[0.7636657953262329]], [[0.5478187799453735]], [[1.22959303855896]], [[-1.6629095077514648]], [[-0.5278839468955994]], [[0.15898258984088898]], [[1.000220775604248]], [[1.083420991897583]], [[-1.2720608711242676]], [[0.23574863374233246]], [[0.4396874010562897]], [[0.7104308605194092]], [[0.6883097887039185]], [[-1.5120340585708618]], [[-0.27725064754486084]], [[1.1652095317840576]], [[0.6678454279899597]], [[0.4044547379016876]], [[-1.588853120803833]], [[0.10982409119606018]], [[0.7705152034759521]], [[0.7239919900894165]], [[0.5325650572776794]], [[-2.0845890045166016]], [[0.9196359515190125]], [[0.8362395167350769]], [[0.6898489594459534]], [[0.07182575017213821]], [[-0.7716994285583496]], [[-0.5762143731117249]], [[0.46993783116340637]], [[0.42921707034111023]], [[0.8545767068862915]], [[-1.801488995552063]], [[0.18351492285728455]], [[1.318638801574707]], [[0.7943655848503113]], [[0.30118492245674133]], [[-0.8504781723022461]], [[-0.19791175425052643]], [[0.7364271879196167]], [[-0.31268006563186646]], [[1.453898549079895]], [[-1.6999027729034424]], [[-0.3689071536064148]], [[0.6285111308097839]], [[0.9383906722068787]], [[0.9711975455284119]], [[-1.2704155445098877]], [[-0.11265530437231064]], [[0.25702616572380066]], [[0.07824986428022385]], [[0.6725088953971863]]], "VALUE_OF_LIFE": 10000000, "INFERRED_WEIGHTAGE_ON_AGENT_HEALTH_INDEX": [0.685, 0.683, 0.718, 0.627, 0.809, 0.808, 0.646, 0.839, 0.765, 0.757, 0.734, 0.889, 0.8150000000000001, 0.769, 0.737, 0.752, 0.75, 0.849, 0.679, 0.875, 0.859, 0.672, 0.733, 0.862, 0.6950000000000001, 0.808, 0.662, 0.855, 0.762, 0.865, 0.687, 0.746, 0.641, 0.847, 0.747, 0.781, 0.795, 0.851, 0.756, 0.673, 0.711, 0.41200000000000003, 0.5690000000000001, 0.787, 0.866, 0.968, 0.872, 0.735, 0.878, 0.754, 0.9380000000000001], "INFERRED_WEIGHTAGE_ON_PLANNER_HEALTH_INDEX": 0.757, "MAX_MARGINAL_AGENT_ECONOMIC_INDEX": [0.0027285723481327295, 0.002734474139288068, 0.0027149636298418045, 0.002733101835474372, 0.002698722528293729, 0.002695730421692133, 0.0026784248184412718, 0.0026936873327940702, 0.0026890540029853582, 0.002731619868427515, 0.0027165759820491076, 0.0027218845207244158, 0.0027180504985153675, 0.0026966372970491648, 0.002704625716432929, 0.002718393225222826, 0.0027127708308398724, 0.002722038421779871, 0.0026984831783920527, 0.0027032513171434402, 0.002696973504498601, 0.0026874069590121508, 0.002697594463825226, 0.0027030734345316887, 0.0027131896931678057, 0.002711491659283638, 0.0027325651608407497, 0.0027140663005411625, 0.002711672568693757, 0.0027134160045534372, 0.0026841098442673683, 0.002694197930395603, 0.0026839885395020247, 0.002705829916521907, 0.00271249539218843, 0.002696133917197585, 0.0027130150701850653, 0.0027131501119583845, 0.0026947411242872477, 0.002690640976652503, 0.002726985840126872, 0.002733224770054221, 0.002736929804086685, 0.0027190325781702995, 0.0027239227201789618, 0.0027165571227669716, 0.0027152288239449263, 0.0027226179372519255, 0.0027034329250454903, 0.0027189261745661497, 0.0027153033297508955], "MAX_MARGINAL_PLANNER_ECONOMIC_INDEX": 0.00270779593847692, "MAX_MARGINAL_AGENT_HEALTH_INDEX": [-3.0090674044913612e-05, -4.101892773178406e-05, -2.391263478784822e-05, -5.501513078343123e-05, -3.641096918727271e-05, -8.148177585098892e-05, -0.00033768793218769133, -8.136823453241959e-05, -0.00017953713540919125, -5.476094884215854e-05, -6.003917951602489e-05, -1.9584411347750574e-05, -2.692215821298305e-05, -8.299046021420509e-05, -7.80503178248182e-05, -1.1469884157122578e-05, -2.996458533743862e-05, -3.8060217775637284e-05, -0.00028306362219154835, -4.246145545039326e-05, -5.934169530519284e-05, -0.0002345900284126401, -0.00020272599067538977, -1.6491414498887025e-05, -4.7763867769390345e-05, -2.500748996681068e-05, -5.226726716500707e-05, -9.981938092096243e-06, -5.143717862665653e-05, -2.055184631899465e-05, -0.0005827346467413008, -7.382390322163701e-05, -0.0012094955891370773, -1.7349766494589858e-05, -8.910564247344155e-06, -4.672993964049965e-05, -2.3844344468670897e-05, -1.9828426957246847e-05, -6.0026035498594865e-05, -0.00021144225320313126, -3.511341492412612e-05, -2.745154961303342e-05, -0.00011554994125617668, -2.7730202418752015e-05, -6.093723641242832e-06, -4.6192471927497536e-05, -2.1770805687992834e-05, -0.0001274808746529743, -8.22813126433175e-06, -3.4170137951150537e-05, -1.28951739952754e-06], "MAX_MARGINAL_PLANNER_HEALTH_INDEX": -0.0001451095740776509, "MIN_MARGINAL_AGENT_ECONOMIC_INDEX": [0.0020356886088848114, 0.0022419975139200687, 0.002160344272851944, 0.0024188596289604902, 0.0020349116530269384, 0.002246933989226818, 0.002235130639746785, 0.0022626728750765324, 0.002382050035521388, 0.0021890902426093817, 0.0021741720847785473, 0.0017624523025006056, 0.0023508218582719564, 0.001991693861782551, 0.0019486609380692244, 0.002157807582989335, 0.00225676316767931, 0.0023168111220002174, 0.002234051935374737, 0.002510908292606473, 0.0023535331711173058, 0.0017812215955927968, 0.0017329421825706959, 0.0023532791528850794, 0.002227779245004058, 0.0022383893374353647, 0.0023805166129022837, 0.0024882552679628134, 0.0010899916524067521, 0.0021604555658996105, 0.001897018519230187, 0.0024196451995521784, 0.001992126228287816, 0.0022069797851145267, 0.001977990148589015, 0.002120157005265355, 0.0020803255029022694, 0.002108931075781584, 0.0019574600737541914, 0.0020899344235658646, 0.002084167208522558, 0.002109887544065714, 0.0022026910446584225, 0.0022563086822628975, 0.002252459991723299, 0.0023787757381796837, 0.002311331918463111, 0.0021701548248529434, 0.002184213837608695, 0.0020549052860587835, 0.0025167958810925484], "MIN_MARGINAL_PLANNER_ECONOMIC_INDEX": 0.002124679973348975, "MIN_MARGINAL_AGENT_HEALTH_INDEX": [-0.003964281640946865, -0.0010713868541643023, -0.0076567428186535835, -0.002871995558962226, -0.008433903567492962, -0.00899407546967268, -0.009911752305924892, -0.009194649755954742, -0.009664129465818405, -0.002949925372377038, -0.0065671540796756744, -0.007176358252763748, -0.006536103319376707, -0.008727999404072762, -0.007968118414282799, -0.006505947560071945, -0.007439949084073305, -0.0069662826135754585, -0.008316239342093468, -0.008579589426517487, -0.00872408039867878, -0.009386059828102589, -0.00945289060473442, -0.00848479475826025, -0.00750832399353385, -0.00821556244045496, -0.0032150805927813053, -0.007467340212315321, -0.008297407068312168, -0.00770018994808197, -0.009744877927005291, -0.00937261339277029, -0.009218289516866207, -0.00839361734688282, -0.007491708733141422, -0.009187941439449787, -0.007287331856787205, -0.007290090434253216, -0.009112140163779259, -0.009490792639553547, -0.004297951702028513, -0.0013750526122748852, -0.0013287087203934789, -0.0064947898499667645, -0.005829110741615295, -0.006879822351038456, -0.006967827212065458, -0.00535054225474596, -0.008951004594564438, -0.006295738276094198, -0.007645895704627037], "MIN_MARGINAL_PLANNER_HEALTH_INDEX": -0.007381112780421972} \ No newline at end of file diff --git a/ai_economist/datasets/covid19_datasets/data_and_fitted_params/model_constants.json b/ai_economist/datasets/covid19_datasets/data_and_fitted_params/model_constants.json new file mode 100644 index 0000000..faff7ff --- /dev/null +++ b/ai_economist/datasets/covid19_datasets/data_and_fitted_params/model_constants.json @@ -0,0 +1 @@ +{"DATE_FORMAT": "%Y-%m-%d", "STRINGENCY_POLICY_KEY": "StringencyIndex", "NUM_STRINGENCY_LEVELS": 10, "SIR_SMOOTHING_STD": 10, "SIR_MORTALITY": 0.02, "SIR_GAMMA": 0.07142857142857142, "US_STATE_IDX_TO_STATE_NAME": {"0": "Alabama", "1": "Alaska", "2": "Arizona", "3": "Arkansas", "4": "California", "5": "Colorado", "6": "Connecticut", "7": "Delaware", "8": "District of Columbia", "9": "Florida", "10": "Georgia", "11": "Hawaii", "12": "Idaho", "13": "Illinois", "14": "Indiana", "15": "Iowa", "16": "Kansas", "17": "Kentucky", "18": "Louisiana", "19": "Maine", "20": "Maryland", "21": "Massachusetts", "22": "Michigan", "23": "Minnesota", "24": "Mississippi", "25": "Missouri", "26": "Montana", "27": "Nebraska", "28": "Nevada", "29": "New Hampshire", "30": "New Jersey", "31": "New Mexico", "32": "New York", "33": "North Carolina", "34": "North Dakota", "35": "Ohio", "36": "Oklahoma", "37": "Oregon", "38": "Pennsylvania", "39": "Rhode Island", "40": "South Carolina", "41": "South Dakota", "42": "Tennessee", "43": "Texas", "44": "Utah", "45": "Vermont", "46": "Virginia", "47": "Washington", "48": "West Virginia", "49": "Wisconsin", "50": "Wyoming"}, "US_STATE_POPULATION": [4903185, 740995, 7278717, 3017804, 39512223, 5758736, 3565287, 973764, 705749, 21477737, 10617423, 1415872, 1787065, 12671821, 6732219, 3155070, 2913314, 4467673, 4648794, 1344212, 6045680, 6892503, 9986857, 5639632, 2976149, 6626371, 1068778, 1934408, 3080156, 1359711, 8882190, 2096829, 19453561, 10488084, 762062, 11689100, 3956971, 4217737, 12801989, 1059361, 5148714, 884659, 6829174, 28995881, 3205958, 623989, 8535519, 7614893, 1792147, 5822434, 578759], "US_POPULATION": 328737916, "GDP_PER_CAPITA": 65300} \ No newline at end of file diff --git a/ai_economist/datasets/covid19_datasets/data_and_fitted_params/real_world_data.npz b/ai_economist/datasets/covid19_datasets/data_and_fitted_params/real_world_data.npz new file mode 100644 index 0000000000000000000000000000000000000000..93ef4088853a427b35d2fbb750069e21e0c366e3 GIT binary patch literal 2120354 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%0D+0~W!wXzYK= zf$#3YD?gaxFUC{a-zR-BidnbVjmdqU2LBx6Uwj4l3h))+E5KKPuK-^Gz5;v&_zL{j zDlj0Q*smOV3LZWOA75fph3xr;>3FW8G^zy0rkMd(^A(ayP^yTWrtX|9uel_fyNJYdA*72jf6-!!97nPj&o!(Yfjmb%2+xfW`(rMn z`WGCjyeDR~63**{+^6~{R4P(o`S%w@)d`^}F$({>Xcc>dX&!)E;Q+E!G{0mBZtF!&)7AGTKe z2|q1D?j?E(n>!-^x-%Kt?12^yFtDqDz28C~8ks>Wdzdf-j*WngaZq#wUMzsR#jySF zCH}=%fUf{w0lor!1^5c^72qqtSAeg;f2;zHdDQ+JiyxxT7k{|H)}(;`U2sU&zdNIE zuX~ApT^b{Mj=^UrDVE)bkXg^(FMivA&zXxpTr>=Qw`7CNY52Z@x_@v#g0x)r`~pk# z)%gR^M~k+if3~^}CF{{Yw|qta>@NF005PFIR!c))Eb5JZ_+MMT$ogV;j`cxSR+w|h zKZd?IITHPF@KW@{E&=F+B@@vH+ohrpwi|*zSM(KquDcHUTD3Ivu~v1kCJKG5)C2vi zMql))f&I~sx@e>Cl-`i_qn}|@7xb6=`=MVfZiU%J=nLH|(f^57pzn)$1l`x6&odZ` z{>(iCHf_c{#G);*pdLQ`A=@8e!0J(-3|-rxfdl3x0wpj&8FLd44PoLo=-L8{r}bl> zll=yIZ#HJ@u9_yywlOe04R#uA%JvH%L;1I`@m+tmUojSQ7e2wTlcqV_-+k4B=_Ve) zTp5Y`3mj7hvh_`KE9SxpuzCl~`~dx@TC?NXSKx1pL2SKv7mO@`uFql3D=6nYn4Krg zf#IEO*!t@LxH2E^Y=j^B4`IhQu7parphj<7w(l|qs!xRXgP`{r7@&f=kE0Xes3ds& zo^0I)`{>%S&-*bBI)uVm+3@E>`1~6TP#wn3AFT(Uje|k5=U84ঘ{BbllVq(Aa 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

H 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

#XfNAD{y$DU{@{Y$h3Lr> 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

mcaL~;LY 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 ~pJi7dvnOx@jz8(~ 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�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≦uA7g8gJkFN24Rn%zXjP4CBB^q?io3}6i0S)qq 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

js{ECKQV8>lV~x933Th1U-X^(H;BTrn2Zd6I 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 literal 0 HcmV?d00001 diff --git a/ai_economist/datasets/covid19_datasets/fit_model_parameters.ipynb b/ai_economist/datasets/covid19_datasets/fit_model_parameters.ipynb new file mode 100644 index 0000000..fcb5107 --- /dev/null +++ b/ai_economist/datasets/covid19_datasets/fit_model_parameters.ipynb @@ -0,0 +1,1450 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Copyright (c) 2021, salesforce.com, inc. \n", + "All rights reserved. \n", + "SPDX-License-Identifier: BSD-3-Clause \n", + "For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# This is a notebook that uses real-world data and fits models to calibrate the COVID-19 simulation environment\n", + "\n", + "### What does it mean to calibrate the simulation?\n", + "\n", + "Simply put, we want to set the parameters of the simulation so that its behavior is consistent with real-world data.\n", + "\n", + "To go into a bit more detail, the point of the simulation is to capture how public health policy decisions influence the spread of COVID-19 and influence the economy.\n", + "We model the spread of COVID-19 through a set of **SIR (+vaccination)** models, one for each US State.\n", + "We model the economy through a model of **unemployment** (again, one for each US State) and relief payments provided by the federal government.\n", + "\n", + "Both the SIR disease dynamics and unemployment respond to public health policy decisions.\n", + "This notebook quantifies those responses as measured in the real world data, and exports that quantification as parameters that the simulation can use.\n", + "\n", + "Another function of the simulator is to quantify **social welfare**, which is the metric that we want to maximize.\n", + "Our simulation defines social welfare as \n", + "$$W = \\alpha H + (1-\\alpha)E$$\n", + "where $H$ and $E$ are indices representing the health outcome (i.e. COVID deaths) and economic outcome (i.e. GDP). \n", + "$\\alpha$ is a weighting term (between 0 and 1).\n", + "\n", + "We don't know what the actual goals/priorities were of real-world policymakers, but we want to calibrate $\\alpha$ (separately for each US State) so that the resulting definition of social welfare is most consistent with public health policy decisions made in the real world." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Dependencies" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import datetime, timedelta\n", + "import json\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import os\n", + "import pickle\n", + "import scipy\n", + "from scipy.signal import convolve\n", + "from scipy.optimize import minimize\n", + "from tqdm import tqdm\n", + "\n", + "import ai_economist" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "### Install torch 1.8.0 or higher (required for the unemployment fits)\n", + "!pip install torch==1.8.0\n", + "\n", + "import torch\n", + "import torch.nn as nn" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Before running this notebook, \n", + "1. Please use the **gather_real_world_data.ipynb** notebook to download and pre-processing the raw real-world data.\n", + "2. Please set the base directory path below; it is the folder into which the real_world data have been downloaded, and it corresponds to the BASE_DATA_DIR_PATH in gather_real_world_data.ipynb notebook (and defaults to \"/tmp/covid19_data\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "BASE_DATA_DIR_PATH = \"/tmp/covid19_data\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Use the latest dated folder in BASE_DATA_DIR_PATH to fetch the real-world data\n", + "Note: if you do not wish to use the latest data, you will need to manually overwrite the data_dir variable" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "data_dir = os.path.join(BASE_DATA_DIR_PATH, sorted(os.listdir(BASE_DATA_DIR_PATH))[-1])\n", + "print(\"In this notebook, we will use the real world data saved in '{}'\".format(data_dir))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Read in the model constants" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "with open(os.path.join(data_dir, \"model_constants.json\"), \"r\") as fp:\n", + " model_constants = json.load(fp)\n", + " \n", + "DATE_FORMAT = model_constants[\"DATE_FORMAT\"]\n", + "STRINGENCY_POLICY_KEY = model_constants[\"STRINGENCY_POLICY_KEY\"]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## This notebook uses the real-world data to calibrate the simulation.\n", + "\n", + "### What does it mean to calibrate the simulation?\n", + "\n", + "Simply put, we want to set the parameters of the simulation so that its behavior is consistent with real-world data.\n", + "\n", + "To go into a bit more detail, the point of the simulation is to capture how public health policy decisions influence the spread of COVID-19 and influence the economy.\n", + "We model the spread of COVID-19 through a set of **SIR (+vaccination)** models, one for each US State.\n", + "We model the economy through a model of **unemployment** (again, one for each US State) and relief payments provided by the federal government.\n", + "\n", + "Both the SIR disease dynamics and unemployment respond to public health policy decisions.\n", + "This notebook quantifies those responses as measured in the real world data, and exports that quantification as parameters that the simulation can use.\n", + "\n", + "Another function of the simulator is to quantify **social welfare**, which is the metric that we want to maximize.\n", + "Our simulation defines social welfare as \n", + "$$W = \\alpha H + (1-\\alpha)E$$\n", + "where $H$ and $E$ are indices representing the health outcome (i.e. COVID deaths) and economic outcome (i.e. GDP). \n", + "$\\alpha$ is a weighting term (between 0 and 1).\n", + "\n", + "We don't know what the actual goals/priorities were of real-world policymakers, but we want to calibrate $\\alpha$ (separately for each US State) so that the resulting definition of social welfare is most consistent with public health policy decisions made in the real world." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Set data / fit parameters:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Set up dictionary to write fitted parameters\n", + "fitted_params_dict = {'settings': {}}\n", + "fitted_params_filename = \"fitted_params.json\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "First, choose the dates we'll use to split the data.\n", + "\n", + "- Data until the last date for **training** can be used to directly fit parameters.\n", + "\n", + "- Date until the last date for **validation** can be used for measuring fit quality.\n", + "\n", + "Our default calibration settings use data in 2020 for fitting (training+validation) so that data in 2021 can be held out as a test set." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Do fitting up until the last day in the train set:\n", + "fitted_params_dict['settings']['LAST_DATE_IN_TRAIN_SET'] = '2020-11-30'\n", + "# Cross validation should uses non-training data up until:\n", + "fitted_params_dict['settings']['LAST_DATE_IN_VAL_SET'] = '2020-12-31'" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Next, set some parameters governing how the SIR and unemployment models use their data. The provided default settings yield good fits for the default date range (above)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Length of the convolutional filters to use in the unemployment fitting\n", + "fitted_params_dict['settings']['FILTER_SIZE_UNEMPLOYMENT'] = 600\n", + "# Weight of regularization term to enforce state-by-state similarity in unemployment fits\n", + "fitted_params_dict['settings']['SIMILARITY_REGULARIZATION_UNEMPLOYMENT'] = 0.5\n", + "\n", + "# Weight of regularization term to enforce state-by-state similarity in Beta fits (for SIR model)\n", + "fitted_params_dict['settings']['SIMILARITY_REGULARIZATION_SIR'] = 1.0" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Lastly, set the environment configuration that we will use when we calibrate $\\alpha$ values (which determine how social welfare is computed).\n", + "\n", + "Note! $\\alpha$ calibration is sensitive to these choices, so we will want to take care to use these same settings between calibration and any downstream experiments." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Env settings for calibrating alphas. Default settings reflect env defaults.\n", + "fitted_params_dict['settings']['env'] = {\n", + " 'economic_reward_crra_eta': 2,\n", + " \"start_date\": '2020-03-22',\n", + " \"infection_too_sick_to_work_rate\": 0.1,\n", + " \"pop_between_age_18_65\": 0.6,\n", + " \"risk_free_interest_rate\": 0.03,\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Load real_world_data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "dataframes = pickle.load( open(os.path.join(data_dir, \"dataframes.pkl\"), \"rb\" ) )" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# 1. Beta Fits\n", + "\n", + "Here, we calibrate the **SIR** model.\n", + "\n", + "Within the SIR model, the **beta** parameter controls the rate that infections spread from infected people to susceptible people. It reflects the probability of disease transmission when a member from each group come in contact, and the rate at which those groups come into contact.\n", + "\n", + "As such, the public health policy can affect beta (intuitively, through its influence on how often people interact with one another).\n", + "\n", + "Our simulation models the relationship between public health policy (i.e. \"stringency\") and beta via a time-delayed linear model. \n", + "Specifically, we model beta of US State $i$ at time $t$ as\n", + "\n", + "$$\\beta_{i,t} = \\beta_{i}^{\\pi} \\cdot \\pi_{i,t-d} + \\beta_{i}^{0},$$\n", + "\n", + "where $\\pi_{i,t-d}$ is the stringency level $d$ days ago, $\\beta_{i}^{\\pi}$ is the slope, and $\\beta_{i}^{0}$ is the intercept.\n", + "\n", + " \n", + " \n", + "The next block of code finds the time delay $d$ and then fits the slope and intercept parameters for each US State. \n", + "Each State uses the same delay.\n", + "In addition, we regularize the State-to-State differences in slope/intercept parameters to help reduce overfitting to noise." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**NOTE!** Although we're directly fitting linear models of beta, our ground-truth data is COVID-19 deaths. The best test of this fit is how well our simulation reproduces the COVID-19 death data given the real-world policy choices." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# This is the subset of the data we will use:\n", + "beta_df = dataframes[\"beta\"]\n", + "policy_df = dataframes[\"policy\"]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Find the optimal delay in the response of beta (IT SHOULD BE >0)\n", + "\n", + "Step through many possible policy delays to find where the empirical correlation between delayed policy and beta is strongest. Since all US States will use the same delay, we aggregate the data across states.\n", + "\n", + "Intuitively, we expect higher stringency to lead to smaller beta, so we're actually looking for the delay where the correlation is smallest (i.e. most negative)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "delays = list(range(-90, 90))\n", + "\n", + "fits = []\n", + "\n", + "for delay in delays:\n", + " D = fitted_params_dict['settings']['LAST_DATE_IN_VAL_SET']\n", + " if delay < 0:\n", + " x = policy_df[:D].values[-delay:].flatten()\n", + " y = beta_df[:D].values[:delay].flatten()\n", + " elif delay == 0:\n", + " x = policy_df[:D].values.flatten()\n", + " y = beta_df[:D].values.flatten()\n", + " else:\n", + " x = policy_df[:D].values[:-delay].flatten()\n", + " y = beta_df[:D].values[delay:].flatten()\n", + " keep = np.logical_not(np.logical_or(np.isnan(y), np.isnan(x)))\n", + "\n", + " x = x[keep]\n", + " y = y[keep]\n", + " fit = scipy.stats.linregress(x, y)\n", + " \n", + " fits.append(fit)\n", + "\n", + "_, (ax0, ax1) = plt.subplots(1, 2, figsize=(16, 6));\n", + "ax0.plot(delays, [f.rvalue for f in fits]);\n", + "ax0.set_xlabel('Policy-vs-Beta Delay');\n", + "ax0.set_ylabel('Correlation r-value');\n", + "\n", + "ax1.plot(delays, [f.slope for f in fits]);\n", + "ax1.set_xlabel('Policy-vs-Beta Delay');\n", + "ax1.set_ylabel('Slope of Linear Fit');\n", + "\n", + "BETA_DELAY = delays[np.argmin(np.array([f.rvalue for f in fits]))]\n", + "ax0.set_title('Optimal delay = {}'.format(BETA_DELAY));" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### What does the aggregate policy vs. beta trend look like at this delay?\n", + "\n", + "This plot shows the raw policy & beta data (blue) after applying the delay. The black dashed line shows the linear fit. The red points show the beta mean at each of the stringency levels." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert BETA_DELAY > 0\n", + "\n", + "x = policy_df[:fitted_params_dict['settings']['LAST_DATE_IN_VAL_SET']].values[:-BETA_DELAY].flatten()\n", + "y = beta_df[:fitted_params_dict['settings']['LAST_DATE_IN_VAL_SET']].values[BETA_DELAY:].flatten()\n", + "keep = np.logical_not(np.logical_or(np.isnan(y), np.isnan(x)))\n", + "\n", + "x = x[keep]\n", + "y = y[keep]\n", + "fit = scipy.stats.linregress(x, y)\n", + "\n", + "print('Fit Intercept: {:+f}'.format(fit.intercept))\n", + "print('Fit Slope: {:+f}'.format(fit.slope))\n", + "\n", + "_, ax = plt.subplots(1, 1, figsize=(8, 7))\n", + "\n", + "ax.plot(x, y, 'o', alpha=0.03);\n", + "xL = ax.get_xlim()\n", + "ax.plot(xL, [fit.intercept + fit.slope*x_ for x_ in xL], 'k--', linewidth=5);\n", + "ax.set_xlim(xL);\n", + "\n", + "xs = np.sort(np.unique(x))\n", + "ys = np.zeros_like(xs, dtype=np.float)\n", + "for i, x_ in enumerate(xs):\n", + " ys[i] = np.nanmean(y[x==x_])\n", + "\n", + "ax.plot(xs, ys, 'ro-', markersize=13);\n", + "\n", + "ax.set_ylim([0, 2*np.nanmax(ys)]);\n", + "ax.set_xlabel('Policy ({})'.format(STRINGENCY_POLICY_KEY), fontsize=16);\n", + "ax.set_ylabel('Estimated Beta ({} days later)'.format(BETA_DELAY), fontsize=16);\n", + "ax.set_title('Effect of Policy on Transmission Rate', fontsize=20);\n", + "ax.grid(b=True, axis='y');" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This code sets up the regularized, State-by-State linear regression. It separates data for training/validation and defines the fitting procedure." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def shift_datestring(dstring, delta):\n", + " return datetime.strftime(\n", + " datetime.strptime(dstring, DATE_FORMAT) + timedelta(delta), DATE_FORMAT\n", + " )\n", + "\n", + "x_train_d0 = '2020-01-01'\n", + "x_train_dT = fitted_params_dict['settings']['LAST_DATE_IN_TRAIN_SET']\n", + "x_val_dT = fitted_params_dict['settings']['LAST_DATE_IN_VAL_SET']\n", + "y_train_d0 = shift_datestring(x_train_d0, BETA_DELAY)\n", + "y_train_dT = shift_datestring(x_train_dT, BETA_DELAY)\n", + "y_val_dT = shift_datestring(x_val_dT, BETA_DELAY)\n", + "\n", + "x_data = policy_df[x_train_d0:x_train_dT].values.T\n", + "y_data = beta_df[y_train_d0:y_train_dT].values.T\n", + "\n", + "x_data_val = policy_df[x_train_dT:x_val_dT].values.T\n", + "y_data_val = beta_df[y_train_dT:y_val_dT].values.T\n", + "\n", + "n_states = x_data.shape[0]\n", + "\n", + "def predict_fn(x, weights):\n", + " slopes = weights[:n_states, None]\n", + " intercepts = weights[n_states:, None]\n", + " return x*slopes + intercepts\n", + "\n", + "def loss_fn(weights, w_sse_lambda): \n", + " y_hat = predict_fn(x_data, weights)\n", + " y_sse = np.nansum((y_data - y_hat)**2)\n", + " \n", + " slopes = weights[:n_states]\n", + " intercepts = weights[n_states:]\n", + " s_sse = np.sum((slopes - np.mean(slopes))**2)\n", + " i_sse = np.sum((intercepts - np.mean(intercepts))**2)\n", + " \n", + " return y_sse + w_sse_lambda*(s_sse*np.nanmean(x_data) + i_sse)\n", + " \n", + "def do_fit(w_sse_lambda):\n", + " w_bounds = [(None, 0)] * n_states\n", + " i_bounds = [(0, None)] * n_states\n", + " res = minimize(\n", + " loss_fn,\n", + " np.zeros(n_states * 2),\n", + " args=(w_sse_lambda),\n", + " bounds=w_bounds + i_bounds,\n", + " )\n", + "\n", + " weights = res.x\n", + " slopes = weights[:n_states]\n", + " intercepts = weights[n_states:]\n", + " y_hat = predict_fn(x_data, weights)\n", + " \n", + " return weights, slopes, intercepts, y_hat" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This code gives us a look at the fit parameters/quality for different regularization levels (lambda).\n", + "\n", + "Notice how stronger regularization leads to more State-by-State similarity across the fit parameters. This also leads to slightly worse training $r^2$ and slightly better validation $r^2$, as one would typically expect.\n", + "\n", + "For the default data split, validation $r^2$ is poor, but, as described above, we are more interested in fitting the *SIR model* (not just fitting betas). So, calibration settings should focus on how well the simulation predicts real-world COVID-19 deaths given the real-world policy." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "_, ax = plt.subplots(1, 1, figsize=(16, 6))\n", + "_, axes = plt.subplots(2, 2, figsize=(16, 8))\n", + "ax0, ax1, ax2, ax3 = axes.flatten()\n", + "\n", + "ax.plot(y_data.flatten(), label='Real Data');\n", + "\n", + "# (regularization_amount, plot_color, plot_style)\n", + "plot_specs = [\n", + " ( 0.0, 'r', ':'),\n", + " ( 5.0, 'g', '--'),\n", + " (10.0, 'c', '-.'),\n", + "]\n", + "for LAMBDA, color, linestyle in plot_specs:\n", + " weights, slopes, intercepts, y_hat = do_fit(w_sse_lambda=LAMBDA)\n", + " y_hat_val = predict_fn(x_data_val, weights)\n", + " print('lambda = {:5.1f}, r^2 = {:5.3f}, r^2 (val) = {:5.3f}'.format(\n", + " LAMBDA,\n", + " 1 - np.nanvar((y_hat - y_data)) / np.nanvar(y_data),\n", + " 1 - np.nanvar((y_hat_val - y_data_val)) / np.nanvar(y_data_val)\n", + " ))\n", + " \n", + " ax.plot(y_hat.flatten(), color=color, linestyle=linestyle, label='Predicted (lambda={})'.format(LAMBDA));\n", + " ax0.plot(slopes, color=color, linestyle=linestyle, label='lambda={}'.format(LAMBDA));\n", + " ax1.plot(intercepts, color=color, linestyle=linestyle, label='lambda={}'.format(LAMBDA));\n", + " ax2.plot(intercepts+slopes, color=color, linestyle=linestyle, label='lambda={}'.format(LAMBDA));\n", + " ax3.plot(y_data.flatten(), y_hat.flatten(), 'o', color=color, alpha=0.1, label='lambda={}'.format(LAMBDA));\n", + "\n", + "LIM = [0, 0.3]\n", + "ax3.plot(LIM, LIM, 'k--');\n", + "ax3.set_xlim(LIM);\n", + "ax3.set_ylim(LIM);\n", + "\n", + "for ax_ in [ax, ax0, ax1, ax2, ax3]:\n", + " ax_.legend();\n", + " \n", + "ax.set_ylabel('Beta');\n", + "ax0.set_ylabel('Slope');\n", + "ax1.set_ylabel('Intercept');\n", + "ax2.set_ylabel('Beta @ stringency=1');\n", + "ax3.set_ylabel('Predicted Beta');\n", + "\n", + "ax0.set_xlabel('State Index');\n", + "ax1.set_xlabel('State Index');\n", + "ax2.set_xlabel('State Index');\n", + "ax3.set_xlabel('Actual Beta');" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Perform the actual calibration fit" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# (if you want to tweak the regularization setting, you can do so here)\n", + "# fitted_params_dict['settings']['SIMILARITY_REGULARIZATION_SIR'] = ...\n", + "\n", + "_, slopes, intercepts, y_hat = do_fit(\n", + " w_sse_lambda=fitted_params_dict['settings']['SIMILARITY_REGULARIZATION_SIR']\n", + ")\n", + "\n", + "# Update the calibration fit dictionary\n", + "fitted_params_dict.update(\n", + " {\n", + " \"BETA_DELAY\": BETA_DELAY,\n", + " \"BETA_SLOPES\": slopes.tolist(),\n", + " \"BETA_INTERCEPTS\": intercepts.tolist()\n", + " }\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# 2. Unemployment Fits\n", + "\n", + "Here, we calibrate the **unemployment** model.\n", + "\n", + "The simulation models unemployment using a time-series model that predicts a State’s unemployment rate based on its history of stringency level increases and decreases.\n", + "\n", + "Each change triggers a combination of exponentially-decaying unemployment responses. We learn a shared set of those responses by learning a set of 5 decay lambdas. Each State separately fits its own model that says strongly each of those underlying responses is triggered by a change in stringency.\n", + "\n", + "In math, the unemployment model looks like this:\n", + "\n", + "$$\n", + "\\tilde{U}_{i,t}^k = \\sum_{t'=t-L}^{t'=t} e^{\\frac{t'-t}{\\lambda_k}} \\cdot \\Delta \\pi_{i, t'}, \\\\\n", + "U_{i,t} = \\texttt{softplus}\\left( \\sum_k w_{i, k} \\cdot \\tilde{U}_{i,t}^k \\right) + U^0_i.\n", + "$$\n", + "\n", + "$\\tilde{U}_{i,t}^k$ describes the unemployment driven by the $k$-th component of the response. $\\lambda_k$ is the decay constant for that component. $L$ is the total length of the response filters. And $\\Delta \\pi_{i,t}$ is how much the stringency changed in State $i$ on time $t$.\n", + "\n", + "$U_{i,t}$ is the actual unemployment according to the model. It adds baseline unemployment $U^0_i$ to excess unemployment coming from the policy choices. Excess unemployment is a State-specific weighted sum of each $\\tilde{U}_{i,t}^k$, with weights $w_{i,k}$, passed through a $\\texttt{softplus}$ function so that excess unemployment is $\\geq 0$.\n", + "\n", + "Similar to the way we fit beta (above), we regularize State-to-State variability in $w_{i,k}$." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# This is the subset of the data we will use for calibrating the unemployment model\n", + "unemployment_df = dataframes[\"unemployment\"]\n", + "policy_df = dataframes[\"policy\"]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Model set-up\n", + "\n", + "For a model like this, we take advantage of the tools available through PyTorch. In particular, we instantiate the unemployment model as a PyTorch module, and write a simple wrapper to handle the data feeding, parameter updates, etc." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "class SharedConvUnemp(nn.Module):\n", + " \"\"\"\n", + " The unemployment model.\n", + " Given a history of stringency changes, predicts current unemployment.\n", + " Embeds the full set of shared and State-specific model weights.\n", + " \"\"\"\n", + " def __init__(\n", + " self, \n", + " x_dim,\n", + " n_filters,\n", + " filter_size,\n", + " n_states=51,\n", + " signal_bias=True,\n", + " initial_lambda_guess=None,\n", + " ):\n", + " super().__init__()\n", + " self.x_dim = int(x_dim)\n", + " self.n_filters = int(n_filters)\n", + " self.filter_size = int(filter_size)\n", + " self.n_states = int(n_states)\n", + " \n", + " # We can use grouped 1D convolution to do state-specific projection of x --> signal\n", + " # Expects input of size [1, x_dim*n_states, time+filter_size] (+filter_size implies pre-padding)\n", + " # Output has size [1, n_filters*n_states, time+filter_size]\n", + " self.signal = nn.Conv1d(\n", + " in_channels=self.n_states*self.x_dim,\n", + " out_channels=self.n_states*self.n_filters,\n", + " kernel_size=1,\n", + " groups=self.n_states,\n", + " bias=bool(signal_bias),\n", + " )\n", + " \n", + " # Convolve the learned \"signal\" with a shared bank of exponential filters w/ learnable lambdas\n", + " # Expects input of size [n_states, n_filters, time+filter_size] (+filter_size implies pre-padding)\n", + " # Output has size [n_states, 1, time+filter_size] (here, +filter_size comes from internal padding)\n", + " self.conv_lambdas = nn.Parameter(initial_lambda_guess,\n", + " requires_grad=True\n", + " )\n", + " self.f_ts = torch.tile(\n", + " torch.flip(torch.arange(self.filter_size), (0,))[None, None],\n", + " (1, self.n_filters, 1)\n", + " )\n", + " \n", + " # State-specific unemployment offsets\n", + " self.unemp_bias = nn.Parameter(\n", + " torch.tensor(np.ones(self.n_states, dtype=np.float32)*3.5),\n", + " requires_grad=True\n", + " )\n", + " \n", + " def get_similarity_regularization_loss(self):\n", + " w = self.signal.weight.flatten().view(self.n_states, -1)\n", + " dev = w - w.mean(0, keepdim=True)\n", + " mse = torch.pow(dev, 2).mean()\n", + " return mse\n", + " \n", + " def x2signal(self, x):\n", + " # Assume input is [n_states, x_dim, time+filter_size], reshape to size expected by self.signal\n", + " x = x.reshape(1, self.n_states*self.x_dim, -1)\n", + " signal = self.signal(x)\n", + " # Output should be returned to [n_states, n_filters, time+filter_size]\n", + " signal = signal.reshape(self.n_states, self.n_filters, -1)\n", + " return signal\n", + " \n", + " def signal2unemp(self, signal):\n", + " # Assume input is [n_states, n_filters, time+filter_size]\n", + " conv_filters = torch.exp(-self.f_ts / self.conv_lambdas[None, :, None])\n", + " unemp = nn.functional.conv1d(signal, conv_filters, padding=self.filter_size-1)\n", + " \n", + " # Output should be returned as [n_states, time] (i.e. we remove padded outputs)\n", + " unemp = unemp.reshape(self.n_states, -1)[:, self.filter_size:-(self.filter_size-1)]\n", + " \n", + " # Soft clipping + baseline unemployment\n", + " return nn.functional.softplus(unemp, beta=1) + self.unemp_bias[:, None]\n", + " \n", + " def forward(self, x):\n", + " signal = self.x2signal(x)\n", + " unemp = self.signal2unemp(signal)\n", + " return unemp" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "class SharedConvUnempFitter:\n", + " \"\"\"\n", + " Wrapper to handle the data feeding and training of the actual unemployment model.\n", + " \"\"\"\n", + " def __init__(self,\n", + " policy_df=None,\n", + " unemployment_df=None,\n", + " lr=0.01, similarity_regularization_coeff=0.0,\n", + " filter_size=600, lambdas=np.array([30, 60, 130, 260, 540])\n", + " ):\n", + " assert unemployment_df is not None\n", + " \n", + " self.n_filters = len(lambdas)\n", + " self.filter_size = int(filter_size)\n", + " self.similarity_regularization_coeff = float(similarity_regularization_coeff)\n", + " self.last_training_time_index = unemployment_df[\n", + " :fitted_params_dict['settings']['LAST_DATE_IN_TRAIN_SET']\n", + " ].shape[0]\n", + " \n", + " # Use this to crop out nans\n", + " keep = np.logical_not(np.isnan(unemployment_df[\n", + " :fitted_params_dict['settings']['LAST_DATE_IN_VAL_SET']\n", + " ].values.T[0]))\n", + " \n", + " # Set up the data\n", + " self.x_data, self.x_th = self.preprocess_policy(policy_df[\n", + " :fitted_params_dict['settings']['LAST_DATE_IN_VAL_SET']\n", + " ].values[keep].T)\n", + " \n", + " self.y_data = unemployment_df[:fitted_params_dict['settings']['LAST_DATE_IN_VAL_SET']].values[keep].T\n", + " self.y_th = torch.from_numpy(self.y_data.astype(np.float32))\n", + " \n", + " # Crop out any nan region\n", + " \n", + " # Create the model\n", + " self.x_dim = self.x_data.shape[1]\n", + " self.model = SharedConvUnemp(\n", + " self.x_dim, self.n_filters, self.filter_size, signal_bias=False,\n", + " initial_lambda_guess=torch.tensor(lambdas.astype(np.float32))\n", + " )\n", + " \n", + " # Loss\n", + " self.loss = nn.MSELoss()\n", + " self.train_loss_history = []\n", + " self.val_loss_history = []\n", + " \n", + " # Create the optimizer\n", + " self.optim = torch.optim.Adam(self.model.parameters(), lr=float(lr))\n", + " \n", + " def preprocess_policy(self, raw_policy_data):\n", + " # Expects policy data size is [n_states, t]\n", + " pad_pol = np.pad(raw_policy_data, [(0, 0), (self.filter_size, 0)], constant_values=1)\n", + " \n", + " dpad = np.zeros_like(pad_pol)\n", + " dpad[:, 1:] = pad_pol[:, 1:] - pad_pol[:, :-1]\n", + "\n", + " x_data = dpad[None]\n", + " \n", + " x_data = x_data.transpose(1, 0, 2)\n", + " x_th = torch.from_numpy(x_data.astype(np.float32))\n", + " \n", + " return x_data, x_th\n", + " \n", + " def predict(self, numpy=True):\n", + " y_hat = self.model(self.x_th.detach())\n", + " if numpy:\n", + " y_hat = y_hat.data.numpy()\n", + " return y_hat\n", + " \n", + " def get_train_loss(self):\n", + " y_hat = self.predict(numpy=False)\n", + " y_hat_train = y_hat[:, :self.last_training_time_index]\n", + " y_train = self.y_th[:, :self.last_training_time_index]\n", + " loss = self.loss(y_hat_train, y_train)\n", + " return loss\n", + " \n", + " def get_val_loss(self):\n", + " y_hat = self.predict(numpy=False)\n", + " y_hat_val = y_hat[:, self.last_training_time_index:]\n", + " y_val = self.y_th[:, self.last_training_time_index:]\n", + " loss = self.loss(y_hat_val, y_val)\n", + " return loss\n", + " \n", + " def get_losses(self):\n", + " y_hat = self.predict(numpy=False)\n", + " \n", + " y_hat_train = y_hat[:, :self.last_training_time_index]\n", + " y_train = self.y_th[:, :self.last_training_time_index]\n", + " train_loss = self.loss(y_hat_train, y_train)\n", + " \n", + " y_hat_val = y_hat[:, self.last_training_time_index:]\n", + " y_val = self.y_th[:, self.last_training_time_index:]\n", + " val_loss = self.loss(y_hat_val, y_val)\n", + " \n", + " return train_loss, val_loss\n", + " \n", + " def update(self):\n", + " self.optim.zero_grad()\n", + " \n", + " # Get the training and val losses\n", + " train_loss, val_loss = self.get_losses()\n", + " self.train_loss_history.append(float(train_loss))\n", + " self.val_loss_history.append(float(val_loss))\n", + " \n", + " # Add any similarity regularization loss\n", + " if self.similarity_regularization_coeff:\n", + " similarity_loss = self.model.get_similarity_regularization_loss()\n", + " train_loss = train_loss + self.similarity_regularization_coeff*similarity_loss\n", + " \n", + " # Update\n", + " train_loss.backward()\n", + " self.optim.step()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Perform the actual calibration fit\n", + "\n", + "Initialize and fit the unemployment model (this will likely take a few minutes)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# (if you want to tweak the regularization setting, you can do so here)\n", + "# fitted_params_dict['settings']['FILTER_SIZE_UNEMPLOYMENT'] = ...\n", + "# fitted_params_dict['settings']['SIMILARITY_REGULARIZATION_UNEMPLOYMENT'] = ...\n", + "\n", + "unemployment_fitter = SharedConvUnempFitter(\n", + " policy_df=policy_df,\n", + " unemployment_df=unemployment_df,\n", + " filter_size=fitted_params_dict['settings']['FILTER_SIZE_UNEMPLOYMENT'],\n", + " similarity_regularization_coeff=fitted_params_dict['settings']['SIMILARITY_REGULARIZATION_UNEMPLOYMENT'],\n", + " lambdas=np.logspace(np.log10(30), np.log10(540), 5),\n", + " lr=0.01,\n", + ")\n", + "\n", + "# Recommend training for 350 steps with lr=0.01 and similarity regularization=0.5\n", + "for _ in tqdm(range(350)):\n", + " unemployment_fitter.update()\n", + "\n", + "_, ax = plt.subplots(1, 1, figsize=(12, 5))\n", + "ax.plot(unemployment_fitter.train_loss_history, label='Training');\n", + "ax.plot(unemployment_fitter.val_loss_history, label='Validation');\n", + "ax.set_ylabel('Loss (MSE)');\n", + "ax.set_xlabel('Training Steps');\n", + "ax.legend();\n", + "ax.set_ylim(bottom=0);\n", + "ax.grid(b=True);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### How well does the predicted US unemployment match the real data?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "_, ax = plt.subplots(1, 1, figsize=(12, 5))\n", + "\n", + "y_hat = unemployment_fitter.predict().mean(0)\n", + "t = np.arange(len(y_hat))\n", + "\n", + "ax.plot(t, unemployment_fitter.y_data.mean(0), 'b', label='Real Data');\n", + "ax.plot(\n", + " t[:unemployment_fitter.last_training_time_index],\n", + " y_hat[:unemployment_fitter.last_training_time_index],\n", + " 'r-', label='Predicted (Train)'\n", + ");\n", + "ax.plot(\n", + " t[unemployment_fitter.last_training_time_index:],\n", + " y_hat[unemployment_fitter.last_training_time_index:],\n", + " 'g-', label='Predicted (Val)'\n", + ");\n", + "ax.grid(b=True);\n", + "\n", + "ax.set_xlabel('Time (days)', fontsize=16);\n", + "ax.set_ylabel('Avg. State Unemployment Rate (%)', fontsize=16);\n", + "ax.set_title('Real vs. Predicted Unemployment', fontsize=20);\n", + "ax.legend(fontsize=20);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### How well does the predicted unemployment match the real data -- *for each state*?\n", + "\n", + "**WARNING: Make sure the unemployment fits above look reasonable for all states before saving!** If not, re-run the fits above" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "scrolled": false + }, + "outputs": [], + "source": [ + "# Note, since there are 51 \"states\", this won't plot Wyoming.\n", + "_, axes = plt.subplots(10, 5, figsize=(16, 40), sharey=True, sharex=True)\n", + "axes = axes.flatten()\n", + "\n", + "y_hat = unemployment_fitter.predict()\n", + "t = np.arange(y_hat.shape[1])\n", + "for IDX, ax in enumerate(axes):\n", + " ax.plot(t, unemployment_fitter.y_data[IDX], 'b-', label='Real Data');\n", + " ax.plot(\n", + " t[:unemployment_fitter.last_training_time_index],\n", + " y_hat[IDX, :unemployment_fitter.last_training_time_index], \n", + " 'r-', label='Predicted (Train)'\n", + " );\n", + " ax.plot(\n", + " t[unemployment_fitter.last_training_time_index:],\n", + " y_hat[IDX, unemployment_fitter.last_training_time_index:],\n", + " 'g-', label='Predicted (Val)'\n", + " );\n", + " ax.grid(b=True, axis='y');\n", + " ax.set_title(unemployment_df.columns[IDX]);\n", + " if ax.is_first_col():\n", + " ax.set_ylabel('Unemployment Rate (%)');\n", + " ax.legend();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## When satisfied with the fits, export them as part of the calibration" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Update fitted_params_dict\n", + "# Note: we cast some arrays as np.float64 in order to be able to write out to a json file\n", + "fitted_params_dict.update(\n", + " {\n", + " \"POLICY_START_DATE\": datetime.strftime(policy_df.index[0], format=DATE_FORMAT),\n", + " \"FILTER_LEN\": unemployment_fitter.filter_size,\n", + " \"CONV_LAMBDAS\": [float(x) for x in unemployment_fitter.model.conv_lambdas.data.numpy()],\n", + " \"UNEMPLOYMENT_BIAS\": [float(x) for x in unemployment_fitter.model.unemp_bias.data.numpy()],\n", + " \"GROUPED_CONVOLUTIONAL_FILTER_WEIGHTS\": unemployment_fitter.model.signal.weight.data.numpy().tolist()\n", + " }\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# 3. Social Welfare\n", + "\n", + "The simulator tracks **social welfare**, which is the metric that we want to maximize.\n", + "Our simulation defines social welfare as \n", + "$W = \\alpha H + (1-\\alpha)E$ \n", + "where $H$ and $E$ are indices representing the health outcome ($H$ decreases with COVID deaths) and economic outcome ($E$ decreases with lost GDP). \n", + "$\\alpha$ is a weighting term (between 0 and 1).\n", + "\n", + "We don't know what the actual goals/priorities were of real-world policymakers, but we want to calibrate $\\alpha$ (separately for each US State) so that the resulting definition of social welfare is most consistent with public health policy decisions made in the real world.\n", + "\n", + "*There is no definitive way to extract these priorities from the data. Our method is just one possible choice.*\n", + "\n", + "The goal of this fitting procedure is to estimate what the outcomes would have been if only interested in either health (minimizing deaths) or in the economy (minimizing GDP loss), and then to compare these against the real-world policy outcomes in order to estimate the underlying policy priorities.\n", + "\n", + "Another way to look at this is that we want to first measure the *shape* of the health/economy trade-off curve for each State. Then, we want to back out the health/economy prioritization (i.e. $\\alpha_i$) that would make the outcomes under the real-world policy preferable to all other outcomes along that trade-off curve.\n", + "\n", + "**We'll go into specifics as we proceed below...**" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Note:** this procedure also gives us the numbers we need to *normalize* $H$ and $E$ so that the Health and Economic Indices have a meaningful scale." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# The env requires the fitted params to run, and we require the env to calibrate the fitted params.\n", + "# Save some placeholders, which we'll update after calibration, then re-save.\n", + "fitted_params_dict.update(\n", + " {\n", + " \"VALUE_OF_LIFE\": 10000000,\n", + " \"INFERRED_WEIGHTAGE_ON_AGENT_HEALTH_INDEX\": [0.5]*51,\n", + " \"INFERRED_WEIGHTAGE_ON_PLANNER_HEALTH_INDEX\": 0.5,\n", + " \"MAX_MARGINAL_AGENT_ECONOMIC_INDEX\": [1]*51,\n", + " \"MAX_MARGINAL_PLANNER_ECONOMIC_INDEX\": 1,\n", + " \"MAX_MARGINAL_AGENT_HEALTH_INDEX\": [1]*51,\n", + " \"MAX_MARGINAL_PLANNER_HEALTH_INDEX\": 1,\n", + " \"MIN_MARGINAL_AGENT_ECONOMIC_INDEX\": [0]*51,\n", + " \"MIN_MARGINAL_PLANNER_ECONOMIC_INDEX\": 0,\n", + " \"MIN_MARGINAL_AGENT_HEALTH_INDEX\": [0]*51,\n", + " \"MIN_MARGINAL_PLANNER_HEALTH_INDEX\": 0,\n", + " }\n", + ")\n", + "with open(os.path.join(data_dir, fitted_params_filename), \"w\") as fp: \n", + " json.dump(fitted_params_dict, fp)\n", + "\n", + "# Define the configuration of the environment that will be built\n", + "N_ALPHA_CALIBRATION_DAYS = (\n", + " datetime.strptime(\n", + " fitted_params_dict['settings']['LAST_DATE_IN_VAL_SET'], DATE_FORMAT\n", + " ) - datetime.strptime(\n", + " fitted_params_dict['settings']['env']['start_date'], DATE_FORMAT\n", + " )\n", + ").days\n", + "\n", + "env_config = {\n", + " \"collate_agent_step_and_reset_data\": True,\n", + " \"scenario_name\": \"CovidAndEconomySimulation\",\n", + " \"path_to_data_and_fitted_params\": data_dir,\n", + " \n", + " \"components\": [\n", + " {\"ControlUSStateOpenCloseStatus\": {\n", + " \"action_cooldown_period\": 28\n", + " }},\n", + " {\"FederalGovernmentSubsidy\": {\n", + " \"num_subsidy_levels\": 20,\n", + " \"subsidy_interval\": 90,\n", + " \"max_annual_subsidy_per_person\": 20000,\n", + " }},\n", + " {\"VaccinationCampaign\": {\n", + " \"daily_vaccines_per_million_people\": 3000,\n", + " \"delivery_interval\": 1,\n", + " \"vaccine_delivery_start_date\": \"2021-01-12\",\n", + " }},\n", + " ],\n", + " \"flatten_masks\": False,\n", + " \"flatten_observations\": False,\n", + " \"health_priority_scaling_agents\": 1.0,\n", + " \"health_priority_scaling_planner\": 1.0,\n", + " \"multi_action_mode_agents\": False,\n", + " \"multi_action_mode_planner\": False,\n", + " \"world_size\": [1, 1],\n", + " \"n_agents\": 51,\n", + " \"episode_length\": N_ALPHA_CALIBRATION_DAYS,\n", + "}\n", + " \n", + "# NOTE!!!!\n", + "# The calibration will be specific to these choices!\n", + "# Downstream environments that use this calibration should also use these parameters!\n", + "env_config.update(fitted_params_dict['settings']['env'])\n", + "\n", + "# Build the environment from this partially-finished calibration.\n", + "uncalibrated_env = ai_economist.foundation.make_env_instance(**env_config)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To estimate $\\hat{\\alpha}_i$ (the hat symbol denotes that this is an estimate), we first collect simulated health/economic outcomes under 3 policies: the actual stringency choices, minimum stringency, and maximum stringency.\n", + "\n", + "\n", + "We use these outcomes to estimate the Pareto frontier in the $\\left( H_i, E_i \\right)$ coordinate space.\n", + "\n", + "\n", + "By definition, the $\\left( H_i, E_i \\right)$ coordinates for the minimum- and maximum-stringency policies define the endpoints of this frontier, at $\\left(0, 1\\right)$ and $\\left(1, 0\\right)$, respectively. \n", + "(This definition comes from how we normalize the health/economic indices -- that is, they are scaled to reflect the range of outcomes spanning the minimum- and maximum-stringency policies.)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Collect the outcomes under the actual policies and 2 extremes: fully-closed and fully-open\n", + "\n", + "index_results = {}\n", + "\n", + "for p in ['closed', 'open', 'actual']:\n", + "\n", + " uncalibrated_env.reset();\n", + " for _ in range(uncalibrated_env.episode_length):\n", + " if p == 'actual':\n", + " t_str = uncalibrated_env.current_date.strftime(DATE_FORMAT)\n", + " actions = {\n", + " str(idx): policy_df[state][t_str]\n", + " for idx, state in uncalibrated_env.us_state_idx_to_state_name.items()\n", + " }\n", + " \n", + " elif p == 'closed':\n", + " actions = {str(idx): 10 for idx in range(51)}\n", + " \n", + " elif p == 'open':\n", + " actions = {str(idx): 1 for idx in range(51)}\n", + " \n", + " else:\n", + " raise NotImplementedError\n", + "\n", + " uncalibrated_env.step(actions);\n", + "\n", + " health_and_economic_indices = {}\n", + " for agent in uncalibrated_env.all_agents:\n", + " health_and_economic_indices[agent.idx] = (\n", + " float(agent.state[\"Health Index\"] / uncalibrated_env.episode_length), \n", + " float(agent.state[\"Economic Index\"] / uncalibrated_env.episode_length),\n", + " )\n", + " \n", + " index_results[p] = health_and_economic_indices" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We assume the Pareto frontier for State $i$ has form $E_i = (1-H_i)^{x_i}$ and that the coordinates associated with the actual-stringency policy are found along this frontier.\n", + "\n", + "We set the shape parameter $x_i$ based on this latter assumption, and take $\\hat{\\alpha}_i$ as the value that maximizes social welfare along the estimated Pareto frontier:\n", + "$$\n", + "\\hat{\\alpha_i} = \\max_{\\alpha_i} \\alpha_i H_i^{\\pi} + \\left(1-\\alpha_i\\right) E_i^{\\pi} =\n", + "\\max_{\\alpha_i} \\alpha_i H_i^{\\pi} + \\left(1-\\alpha_i \\right)\\left(1- \\left(1-H_i^{\\pi}\\right)^{x_i} \\right),\n", + "$$\n", + "\n", + "where the Economic Index $E_i^{\\pi}$ and Health Index $H_i^{\\pi}$ values are obtained from running the policies $\\pi$ in the simulation.\n", + "\n", + "\n", + "In other words, given the estimate of the Pareto frontier, we find the $\\hat{\\alpha}_i$ that best rationalizes the outcomes obtained under the actual policy, i.e. the $\\hat{\\alpha}_i$ under which these outcomes ($E_i^{\\pi}$ and $H_i^{\\pi}$) are considered optimal." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# This function implements the process described above and adds some plotting so we can visualize things\n", + "\n", + "def estimate_alpha_and_plot_rew_examples(state_idx, do_plot=True, ax=None):\n", + " \n", + " act_h, act_e = index_results[\"actual\"][state_idx] # actual health index, actual economic index\n", + " max_h, min_e = index_results[\"closed\"][state_idx] # max health index, min economic index\n", + " min_h, max_e = index_results[\"open\"][state_idx] # min health index, max economic index\n", + "\n", + " norm_idx_pairs = []\n", + " for index_dict in index_results.values():\n", + " h_index, e_index = index_dict[state_idx]\n", + " nh = (h_index-min_h)/(max_h - min_h)\n", + " ne = (e_index-min_e)/(max_e - min_e)\n", + " norm_idx_pairs.append([nh, ne])\n", + " \n", + " # Split out the normalized health / economic indices\n", + " norm_idx_pairs = np.array(norm_idx_pairs)\n", + " nhs = norm_idx_pairs[:, 0]\n", + " nes = norm_idx_pairs[:, 1]\n", + "\n", + " # We assume the coordinates along the pareto curve have this form:\n", + " # (h, e) = (h, (1-h)**pwr)\n", + " # Fit the power terms of the estimated pareto curve\n", + " def loss_fn(pwr):\n", + " nes_hat = (1-(nhs**pwr))**(1/pwr)\n", + " return np.sum((nes_hat - nes)**2)\n", + " res = minimize(\n", + " fun=loss_fn,\n", + " x0=2,\n", + " bounds=[(1.001, None)],\n", + " )\n", + " pwr = res.x\n", + " \n", + " # Given the supplied or fit powers, produce the estimated pareto curve (the set of hs/es coordinates)\n", + " policies = np.linspace(0, 1, 1001)\n", + " hs = policies**(1/pwr)\n", + " es = (1-policies)**(1/pwr)\n", + " \n", + " # Find the alpha such that the optimal nh/ne coordinate is closest to the ACTUAL policy nh/ne coordinate\n", + " nh = (act_h-min_h)/(max_h-min_h)\n", + " ne = (act_e-min_e)/(max_e-min_e)\n", + " alphas = np.linspace(0, 1, 1001)\n", + " d_opt2actual = []\n", + " # For each possible alpha ...\n", + " for alpha in alphas:\n", + " # ... find the optimal nh/ne coordinate for this alpha ...\n", + " opt_nh_ne_index = np.argmax(alpha*hs + (1-alpha)*es)\n", + " opt_nh = hs[opt_nh_ne_index]\n", + " opt_ne = es[opt_nh_ne_index]\n", + " # ... and store its distance to the ACTUAL h/e coordinate.\n", + " d = np.sqrt(((nh-opt_nh)**2) + (ne-opt_ne)**2)\n", + " d_opt2actual.append(d)\n", + " # The inferred alpha is that where the distance measured above is lowest\n", + " alpha = float(alphas[np.argmin(d_opt2actual)])\n", + " \n", + " if not do_plot:\n", + " return alpha\n", + " \n", + " if ax is None:\n", + " _, ax = plt.subplots(1, 1, figsize=(6, 6))\n", + " \n", + " # Plot a reward heatmap for the given alpha\n", + " h_full, e_full = np.meshgrid(np.linspace(0, 1, 101), np.linspace(0, 1, 101))\n", + " r_full = alpha*h_full + (1-alpha)*e_full\n", + " ax.imshow(r_full, aspect='auto', origin='lower')\n", + " if ax.is_last_row():\n", + " ax.set_xlabel('Normalized Health Index');\n", + " if ax.is_first_col():\n", + " ax.set_ylabel('Normalized Economic Index');\n", + " \n", + " # Add the observed h/e coordinates from actual policies\n", + " for nh, ne in norm_idx_pairs:\n", + " ax.plot(nh*100, ne*100, 'bo', markersize=12);\n", + " \n", + " # Add the estimated pareto boundary\n", + " ax.plot(hs*100, es*100, 'w');\n", + " \n", + " # Mark the optimal point along the boundary for the given alpha\n", + " rs = alpha*hs + (1-alpha)*es\n", + " ax.plot(hs[rs.argmax()]*100, es[rs.argmax()]*100, 'o', markerfacecolor=\"None\", \n", + " markersize=8, markeredgecolor='red', markeredgewidth=2);\n", + " \n", + " ax.set_title('{}; alpha={:4.2f}'.format(\n", + " 'USA' if state_idx=='p' else uncalibrated_env.us_state_idx_to_state_name[str(state_idx)],\n", + " alpha,\n", + " ))\n", + " \n", + " ax.set_xticklabels(\n", + " [\"{:3.2f}\".format(x/100).rstrip('0').rstrip('.') for x in ax.get_xticks()]\n", + " )\n", + " ax.set_yticklabels(\n", + " [\"{:3.2f}\".format(y/100).rstrip('0').rstrip('.') for y in ax.get_yticks()]\n", + " )\n", + " \n", + " return alpha" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Here are some visualizations to make things more intuitive...\n", + "\n", + "Blue dots are normalized outcomes from the actual policy, and the fully-closed and fully-open policies.\n", + "\n", + "The white line connecting these dots estimates the pareto frontier.\n", + "\n", + "The red circle is the best outcome on the pareto frontier for the estimated alpha.\n", + "\n", + "The estimated alpha is the one that places the red circle closest to the actual-policy blue dot.\n", + "\n", + "The heatmap background shows the reward at each coordinate (given $\\hat{\\alpha}_i$)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Single-State plot so things are easier to see.\n", + "alpha = estimate_alpha_and_plot_rew_examples(state_idx=50);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "scrolled": false + }, + "outputs": [], + "source": [ + "# Now, for all rest of the States!\n", + "\n", + "_, axes = plt.subplots(10, 5, figsize=(16, 35), sharex=True, sharey=True)\n", + "\n", + "for i, ax in enumerate(axes.flatten()):\n", + " alpha = estimate_alpha_and_plot_rew_examples(state_idx=i, ax=ax)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Perform the actual calibration fit\n", + "\n", + "At the end of this step, we have finalized the fitted_params_dict and are ready to export the final calibration!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# The fully-closed and fully-open policies give us coordinates for normalizing the indices\n", + "fitted_params_dict.update(\n", + " {\n", + " \"MAX_MARGINAL_AGENT_ECONOMIC_INDEX\": [index_results['open'][i][1] for i in range(51)],\n", + " \"MAX_MARGINAL_PLANNER_ECONOMIC_INDEX\": index_results['open']['p'][1],\n", + " \"MAX_MARGINAL_AGENT_HEALTH_INDEX\": [index_results['closed'][i][0] for i in range(51)],\n", + " \"MAX_MARGINAL_PLANNER_HEALTH_INDEX\": index_results['closed']['p'][0],\n", + " \"MIN_MARGINAL_AGENT_ECONOMIC_INDEX\": [index_results['closed'][i][1] for i in range(51)],\n", + " \"MIN_MARGINAL_PLANNER_ECONOMIC_INDEX\": index_results['closed']['p'][1],\n", + " \"MIN_MARGINAL_AGENT_HEALTH_INDEX\": [index_results['open'][i][0] for i in range(51)],\n", + " \"MIN_MARGINAL_PLANNER_HEALTH_INDEX\": index_results['open']['p'][0],\n", + " }\n", + ") \n", + "\n", + "# Apply the alpha-estimation procedure to fill in the rest of the fitted params dictionary\n", + "fitted_params_dict.update(\n", + " {\n", + " \"INFERRED_WEIGHTAGE_ON_AGENT_HEALTH_INDEX\": [\n", + " estimate_alpha_and_plot_rew_examples(state_idx=i, do_plot=False) for i in range(51)\n", + " ],\n", + " \"INFERRED_WEIGHTAGE_ON_PLANNER_HEALTH_INDEX\": estimate_alpha_and_plot_rew_examples(\n", + " state_idx='p', do_plot=False\n", + " ),\n", + " }\n", + ")\n", + "\n", + "# We have replaced the placeholders in the original fitted_params_file. All done -- time to re-save!\n", + "with open(os.path.join(data_dir, fitted_params_filename), \"w\") as fp: \n", + " json.dump(fitted_params_dict, fp)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# \n", + "# \n", + "# Inspection\n", + "\n", + "Use this section to inspect the behavior of the calibrated simulator. As a starting point, we provide some code:\n", + "- to re-build an environment instance (now that all the calibration fits have been exported),\n", + "- to run that environment using the real-world policies,\n", + "- and to compare real-world COVID-19 cases/deaths against what the calibrated simulation predicts given the real-world policies." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run the actual policy in the simulation to see what kind of simulated outcomes we get and how they compare.\n", + "\n", + "# (this will now use the fully-calibrated settings)\n", + "calibrated_env = ai_economist.foundation.make_env_instance(**env_config)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run sim, setting agent actions based on the real-world policy\n", + "calibrated_env.reset();\n", + "for _ in range(calibrated_env.episode_length):\n", + " t_str = calibrated_env.current_date.strftime(DATE_FORMAT)\n", + " actions = {\n", + " str(idx): policy_df[state][t_str]\n", + " for idx, state in calibrated_env.us_state_idx_to_state_name.items()\n", + " }\n", + " calibrated_env.step(actions);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "scrolled": false + }, + "outputs": [], + "source": [ + "# Visualize active cases and deaths, for the real-world and for the simulation w/ real-world policies\n", + "_, (ax0, ax1) = plt.subplots(1, 2, figsize=(16, 5))\n", + "\n", + "\n", + "infected_df = dataframes[\"infected\"]\n", + "deaths_df = dataframes[\"smoothed_deaths\"]\n", + "\n", + "ax0.plot(infected_df[\n", + " fitted_params_dict['settings']['env']['start_date']:fitted_params_dict['settings']['LAST_DATE_IN_VAL_SET']\n", + "].sum(1).values, label='Actual Data');\n", + "ax0.plot(calibrated_env.world.global_state[\"Infected\"][:, :].sum(1), 'r', label='Simulated');\n", + "ax0.set_title('Active COVID-19 Cases');\n", + "ax0.set_xlabel('Days Since Start Date');\n", + "ax0.grid(b=True, axis='y');\n", + "ax0.legend();\n", + "\n", + "ax1.plot(deaths_df[\n", + " fitted_params_dict['settings']['env']['start_date']:fitted_params_dict['settings']['LAST_DATE_IN_VAL_SET']\n", + "].sum(1).values, label='Actual Data');\n", + "ax1.plot(calibrated_env.world.global_state['Deaths'][:, :].sum(1), 'r', label='Simulated');\n", + "ax1.set_title('Cumulative COVID-19 Deaths');\n", + "ax1.set_xlabel('Days Since Start Date');\n", + "ax1.grid(b=True, axis='y');\n", + "ax1.legend();" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.10" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/ai_economist/datasets/covid19_datasets/gather_real_world_data.ipynb b/ai_economist/datasets/covid19_datasets/gather_real_world_data.ipynb new file mode 100644 index 0000000..d6d1990 --- /dev/null +++ b/ai_economist/datasets/covid19_datasets/gather_real_world_data.ipynb @@ -0,0 +1,846 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Copyright (c) 2021, salesforce.com, inc. \n", + "All rights reserved. \n", + "SPDX-License-Identifier: BSD-3-Clause \n", + "For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# This notebook will be used to gather real-world data and perform data processing in order to use it in the covid-19 simulation.\n", + "\n", + "### All the downloaded data will be formatted into pandas dataframes." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Below is the list of COVID-19 data sources used in this notebook\n", + "\n", + "1. **US state government policies** (Oxford Covid-19 Government Response Tracker (OxCGRT))\n", + "\n", + " https://github.com/OxCGRT/USA-covid-policy\n", + "\n", + "\n", + "2. **US federal government direct payments** (Committee for a Responsible Federal Budget)\n", + "\n", + " https://www.covidmoneytracker.org/\n", + " \n", + " https://docs.google.com/spreadsheets/d/1Nr_J5wLfUT4IzqSXkYbdOXrRgEkBxhX0/edit#gid=682404301\n", + " \n", + "\n", + "3. **US deaths data** (COVID-19 Data Repository by the Center for Systems Science and Engineering (CSSE) at Johns Hopkins University)\n", + "\n", + " https://github.com/CSSEGISandData/COVID-19\n", + "\n", + "\n", + "4. **US vaccinations** (Our World in Data)\n", + " \n", + " https://ourworldindata.org/covid-vaccinations\n", + " \n", + " \n", + "5. **US unemployment** (Bureau of Labor and Statistics)\n", + "\n", + " https://www.bls.gov/lau/" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Dependencies" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import datetime, timedelta\n", + "import json\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import os\n", + "import pandas as pd\n", + "import pickle\n", + "import scipy\n", + "from scipy.signal import convolve" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Classes to fetch the real-world data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from ai_economist.datasets.covid19_datasets.us_policies import DatasetCovidPoliciesUS\n", + "from ai_economist.datasets.covid19_datasets.us_deaths import DatasetCovidDeathsUS\n", + "from ai_economist.datasets.covid19_datasets.us_vaccinations import DatasetCovidVaccinationsUS\n", + "from ai_economist.datasets.covid19_datasets.us_unemployment import DatasetCovidUnemploymentUS" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Set a base directory where you would like to download real world data. The latest data will be downloaded into a folder within the base directory, named using the current date" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "BASE_DATA_DIR_PATH = \"/tmp/covid19_data\" # SPECIFY A BASE DIRECTORY TO STORE ALL THE DOWNLOADED DATA" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "DOWNLOAD_LATEST_DATA = True # Download the latest data or use whatever is saved earlier \n", + "CURRENT_DATE = datetime.now()\n", + "DATE_FORMAT = \"%Y-%m-%d\"\n", + "date_string = CURRENT_DATE.strftime(DATE_FORMAT).replace('/','-')\n", + "data_dir = os.path.join(BASE_DATA_DIR_PATH, date_string)\n", + "\n", + "print(\"All the data will be downloaded to the directory: '{}'.\".format(data_dir))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Set up dictionary to write model constants\n", + "model_constants = {}\n", + "model_constants_filename = \"model_constants.json\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Gather real-world data" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# 1. COVID-19 US State Government Policies\n", + "### Source: Oxford Covid-19 Government Response Tracker (OxCGRT) \n", + "(https://github.com/OxCGRT/USA-covid-policy)\n", + "\n", + "**NOTE:** All data will use the same format as **policy_df** (below) and use the same date index" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "covid_policies_us = DatasetCovidPoliciesUS(\n", + " data_dir=data_dir,\n", + " download_latest_data=DOWNLOAD_LATEST_DATA\n", + ")\n", + "\n", + "# Which of the policy indicators to treat as the open/close level\n", + "STRINGENCY_POLICY_KEY = 'StringencyIndex'\n", + "# Number of levels to discretize the stringency policy into. \n", + "# In the context of reinforcement learning, this also determines the action space of the agents.\n", + "NUM_STRINGENCY_LEVELS = 10\n", + "\n", + "policies_us_df = covid_policies_us.process_policy_data(\n", + " stringency_policy_key=STRINGENCY_POLICY_KEY,\n", + " num_stringency_levels=NUM_STRINGENCY_LEVELS\n", + ")\n", + "\n", + "print(\"Policy data are available between {} and {}\".format(policies_us_df[\"Date\"].min(), \n", + " policies_us_df[\"Date\"].max()))\n", + "\n", + "policy_df = policies_us_df.pivot(\n", + " index=\"Date\", columns=\"RegionName\", values=STRINGENCY_POLICY_KEY\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# This is the common date index that all the dataframes will use\n", + "COMMON_DATE_INDEX = policy_df.index" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# This is the list of states (in order) all the dataframes will use\n", + "US_STATE_ORDER = policy_df.columns.values" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Visualize the stringency level for a specified US state\n", + "state = \"California\"\n", + "policy_df[state].plot(figsize=(15,5), x='Date', title=\"Stringency Level for {}\".format(state), grid=True);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# 2. COVID-19 Federal government subsidies (direct payments) to the states\n", + "### Source: Committee For A Responsible Federal Budget\n", + "https://www.covidmoneytracker.org/\n", + "\n", + "### Direct payments provided by the Federal Government so far are recorded in this google spreadsheet\n", + "https://docs.google.com/spreadsheets/d/1Nr_J5wLfUT4IzqSXkYbdOXrRgEkBxhX0/edit#gid=682404301\n", + "### Read as (date: direct payment amount)\n", + "2020-04-15: 274B\n", + "\n", + "2020-12-27: 142B\n", + "\n", + "2021-03-11: 386B" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "subsidy_df = pd.DataFrame(policy_df.index).set_index(\"Date\")\n", + "subsidy_df[\"USA\"] = 0.0\n", + "\n", + "subsidy_df.loc[\"2020-04-15\", \"USA\"] = 274e9\n", + "subsidy_df.loc[\"2020-12-27\", \"USA\"] = 142e9\n", + "subsidy_df.loc[\"2021-03-11\", \"USA\"] = 386e9" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# 3. COVID-19 Deaths data\n", + "### Source: COVID-19 Data Repository by the Center for Systems Science and Engineering (CSSE) at Johns Hopkins University \n", + "(https://github.com/CSSEGISandData/COVID-19)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "deaths_us_df = DatasetCovidDeathsUS(\n", + " data_dir=data_dir,\n", + " download_latest_data=DOWNLOAD_LATEST_DATA\n", + ").df\n", + "\n", + "print(\"COVID-19 death data for the US is available between {} and {}\".format(\n", + " deaths_us_df.columns[12], deaths_us_df.columns[-1]))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Retain just the states in US_STATE_ORDER\n", + "deaths_us_df = deaths_us_df[deaths_us_df.Province_State.isin(US_STATE_ORDER)]\n", + "\n", + "# We will visualize this later in the notebook" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# 4. COVID-19 Vaccination Data\n", + "### Source: Our World in Data\n", + "(https://ourworldindata.org/covid-vaccinations)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "vaccinations_us_df = DatasetCovidVaccinationsUS(\n", + " data_dir=data_dir,\n", + " download_latest_data=DOWNLOAD_LATEST_DATA\n", + ").df\n", + "\n", + "vaccination_dates = sorted(vaccinations_us_df.date.unique())\n", + "print(\"Vaccination data is available between {} and {}\".format(min(vaccination_dates), max(vaccination_dates)))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "vaccinated_df = vaccinations_us_df.pivot(\n", + " index=\"date\", columns=\"location\", values=\"people_fully_vaccinated\"\n", + ")[US_STATE_ORDER]\n", + "\n", + "vaccinated_df.index = pd.to_datetime(vaccinated_df.index)\n", + "vaccinated_df = vaccinated_df.reindex(COMMON_DATE_INDEX).fillna(0)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Visualize the vaccinations for a specified US state\n", + "# Warning: the last value may not be updated (may show it to be 0)\n", + "\n", + "state = \"California\"\n", + "vaccinated_df[state].plot(figsize=(15,5), x='Date', title=\"Vaccinations for {}\".format(state), grid=True);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Using deaths and vaccinations to compute the susceptible-infected-recovered (SIR) numbers\n", + "\n", + "Our SIR data will only treat **deaths** as ground-truth.\n", + "\n", + "Given death data and some assumed constants about the _death rate_ and _recovery rate_ , we can apply some \"SIR algebra\" (i.e. solve for unknowns using the SIR equations) to _infer_ quantities like total \"recovered\", number of infected people, and ultimately **Beta**, which is the rate of transmission times the number of people an infected person comes into contact with." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# For data representation, we will want to build a dataframe for...\n", + "# ... deaths...\n", + "deaths_df = pd.DataFrame(COMMON_DATE_INDEX, columns=['Date']).set_index('Date')\n", + "smoothed_deaths_df = pd.DataFrame(COMMON_DATE_INDEX, columns=['Date']).set_index('Date')\n", + "# ... (inferred) SIR states...\n", + "susceptible_df = pd.DataFrame(COMMON_DATE_INDEX, columns=['Date']).set_index('Date')\n", + "infected_df = pd.DataFrame(COMMON_DATE_INDEX, columns=['Date']).set_index('Date')\n", + "recovered_df = pd.DataFrame(COMMON_DATE_INDEX, columns=['Date']).set_index('Date')\n", + "# ... and (inferred) Beta.\n", + "beta_df = pd.DataFrame(COMMON_DATE_INDEX, columns=['Date']).set_index('Date')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# STD of the Gaussian smoothing window applied to the death data.\n", + "SIR_SMOOTHING_STD = 10" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Fill the death dataframe from (smoothed) raw data\n", + "\n", + "def smooth(x, gauss_std=10):\n", + " \"\"\"\n", + " gauss_std: standard deviation of the Gaussian smoothing window applied to the death data.\n", + " \"\"\"\n", + " if gauss_std <= 0:\n", + " return x\n", + " # To invalidate the near-edge results, bookend the input x with nans\n", + " x = np.concatenate([[np.nan], np.array(x), [np.nan]])\n", + " \n", + " kernel = scipy.stats.norm.pdf(\n", + " np.linspace(-3*gauss_std, 3*gauss_std, 1+6*gauss_std),\n", + " scale=gauss_std\n", + " )\n", + " normer = np.ones_like(x)\n", + " smoothed_x = convolve(x, kernel, mode='same') / convolve(normer, kernel, mode='same')\n", + " \n", + " # Remove the indices added by the nan padding\n", + " return smoothed_x[1:-1]\n", + "\n", + "for us_state_name in US_STATE_ORDER:\n", + " state_deaths = deaths_us_df[deaths_us_df['Province_State']==us_state_name]\n", + " cumulative_state_deaths = []\n", + " for d in COMMON_DATE_INDEX:\n", + " date_string = '{d.month}/{d.day}/{y}'.format(d=d, y=d.year % 2000)\n", + " if date_string in state_deaths:\n", + " cumulative_state_deaths.append(\n", + " state_deaths[date_string].sum()\n", + " )\n", + " else:\n", + " cumulative_state_deaths.append(\n", + " np.nan\n", + " )\n", + " \n", + " # Store raw numbers (for direct comparison)\n", + " deaths_df[us_state_name] = cumulative_state_deaths\n", + " \n", + " # Store smoothed numbers (for beta analysis)\n", + " smoothed_cumulative_state_deaths = smooth(cumulative_state_deaths, gauss_std=SIR_SMOOTHING_STD)\n", + " smoothed_deaths_df[us_state_name] = smoothed_cumulative_state_deaths" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "state_deaths = deaths_us_df[deaths_us_df['Province_State']==\"California\"]\n", + "cumulative_state_deaths = []\n", + "for d in COMMON_DATE_INDEX:\n", + " date_string = '{d.month}/{d.day}/{y}'.format(d=d, y=d.year % 2000)\n", + " if date_string in state_deaths:\n", + " cumulative_state_deaths.append(\n", + " state_deaths[date_string].sum()\n", + " )\n", + " else:\n", + " cumulative_state_deaths.append(\n", + " np.nan\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Visualize the deaths for a specified US state\n", + "state = \"California\"\n", + "\n", + "# Some values near the ends may be \"missing\" because of smoothing\n", + "deaths_df[state].plot(figsize=(15,5), x='Date', ylim=[0, 65000]);\n", + "smoothed_deaths_df[state].plot(figsize=(15,5), x='Date', title=\"COVID deaths in {}\".format(state), ylim=[0, 65000], grid=True);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Death rate: fraction of infected persons who die\n", + "SIR_MORTALITY = 0.02\n", + "\n", + "# Recovery rate: the inverse of expected time someone remains infected\n", + "SIR_GAMMA = 1 / 14" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# This is the core \"SIR algebra\" used to infer S, I, R, and Beta at each date.\n", + "\n", + "def infer_sir_and_beta(us_state_name):\n", + " state_population = deaths_us_df[deaths_us_df['Province_State']==us_state_name]['Population'].sum()\n", + " \n", + " # Helpful to do this math in normalized numbers\n", + " dead = np.array(smoothed_deaths_df[us_state_name]) / state_population\n", + " vaccinated = np.array(vaccinated_df[us_state_name]) / state_population\n", + " \n", + " # Dead is the fraction of \"recovered\" that did not survive\n", + " # Also, the vaccinated lot is part of the recovered\n", + " recovered = dead / SIR_MORTALITY + vaccinated\n", + " \n", + " # The daily change in recovered (ignoring the vaccinated) is a fraction of the infected population on the previous day\n", + " infected = np.nan * np.zeros_like(dead)\n", + " infected[:-1] = (recovered[1:] - recovered[:-1] - (vaccinated[1:] - vaccinated[:-1])) / SIR_GAMMA\n", + " \n", + " # S+I+R must always = 1\n", + " susceptible = 1 - infected - recovered\n", + " \n", + " # Here's where things get interesting. The change in infected is due to...\n", + " change_in_i = infected[1:] - infected[:-1]\n", + " # ... infected people that transition to the recovered state (decreases I)...\n", + " expected_change_from_recovery = -infected[:-1] * SIR_GAMMA\n", + " # ... and susceptible people that transition to the infected state (increases I).\n", + " new_infections = change_in_i - expected_change_from_recovery\n", + " \n", + " # With these pieces, we can solve for Beta.\n", + " beta_ = new_infections / (infected[:-1] * susceptible[:-1] + 1e-6)\n", + " beta_ = np.clip(beta_, 0, 1)\n", + " # Apply a threshold in terms of normalized daily deaths (if too low, beta estimates are bad)\n", + " normalized_daily_deaths = dead[1:]-dead[:-1]\n", + " ndd_lookback = np.zeros_like(new_infections)\n", + " lookback_window = 3*SIR_SMOOTHING_STD\n", + " ndd_cutoff = 1e-8\n", + " ndd_lookback[lookback_window:] = normalized_daily_deaths[:-lookback_window]\n", + " beta_[np.logical_not(ndd_lookback > 1e-8)] = np.nan\n", + " \n", + " beta = np.nan * np.zeros_like(dead)\n", + " beta[:-1] = beta_\n", + " \n", + " # Undo normalization\n", + " susceptible *= state_population\n", + " infected *= state_population\n", + " recovered *= state_population\n", + " \n", + " return susceptible, infected, recovered, beta" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Fill the SIR and Beta dataframes with their inferred values\n", + "for st in US_STATE_ORDER:\n", + " susceptible_df[st], infected_df[st], recovered_df[st], beta_df[st] = infer_sir_and_beta(us_state_name=st)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "## Visualize the SIR and BETA for a specified US state\n", + "# Warning: some values near the ends may be \"missing\" because of smoothing\n", + "\n", + "state = \"California\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "susceptible_df[state].plot(figsize=(15,3), x='Date', title=\"(Inferred) Susceptible Population in {}\".format(state), grid=True);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "infected_df[state].plot(figsize=(15,3), x='Date', title=\"(Inferred) Infected Population in {}\".format(state), grid=True);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "recovered_df[state].plot(figsize=(15,3), x='Date', title=\"(Inferred) Recovered Population in {}\".format(state), grid=True);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "beta_df[state].plot(figsize=(15,3), x='Date', title=\"(Inferred) SIR Beta in {}\".format(state), grid=True);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# 5. COVID-19 Unemployment data\n", + "### Source: Bureau of Labor and Statistics\n", + "\n", + "https://www.bls.gov/lau/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "monthly_unemployment_us = DatasetCovidUnemploymentUS(\n", + " data_dir=data_dir,\n", + " download_latest_data=DOWNLOAD_LATEST_DATA).data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "sample_monthly_unemployment = monthly_unemployment_us['California']\n", + "unemp_year_keys = sorted(sample_monthly_unemployment.keys())\n", + "unemp_starting_month_key = sorted(sample_monthly_unemployment[unemp_year_keys[0]].keys())[0]\n", + "unemp_ending_month_key = sorted(sample_monthly_unemployment[unemp_year_keys[-1]].keys())[-1]\n", + "unemp_starting_date = datetime.strptime(\n", + " str(unemp_year_keys[0]) + '-' + str(unemp_ending_month_key+1) + '-1', DATE_FORMAT)\n", + "unemp_ending_date = datetime.strptime(\n", + " str(unemp_year_keys[-1]) + '-' + str(unemp_ending_month_key+1) + '-1', DATE_FORMAT) - timedelta(1)\n", + "\n", + "print(\"Unemployment data is available between {} and {}\".format(datetime.strftime(unemp_starting_date, DATE_FORMAT),\n", + " datetime.strftime(unemp_ending_date, DATE_FORMAT)))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Convert this to a daily unemployment dataframe\n", + "\n", + "unemployment_df = pd.DataFrame(COMMON_DATE_INDEX, columns=['Date']).set_index('Date')\n", + "\n", + "for us_state_name in monthly_unemployment_us.keys():\n", + " unemployment_df[us_state_name] = [\n", + " monthly_unemployment_us[us_state_name][x.year].get(x.month, np.nan)\n", + " for x in unemployment_df.index\n", + " ]\n", + "unemployment_df = unemployment_df[US_STATE_ORDER]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "## Visualize the unemployment rate for a specified US state\n", + "# There is likely going to be some unemployment data missing at the tail end, \n", + "# as the unemployment data isn't updated as frequently as the other data.\n", + "\n", + "state = \"California\"\n", + "unemployment_df[state].plot(figsize=(15,5), x='Date', title=\"Unemployment for {} (%)\".format(state), grid=True);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Unemployment rate -> unemployed (the number of unemployed people)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "us_state_to_pop_dict = {}\n", + "for us_state in US_STATE_ORDER:\n", + " us_state_to_pop_dict[us_state] = deaths_us_df[deaths_us_df.Province_State==us_state].Population.sum()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "unemployed_df = unemployment_df.multiply([us_state_to_pop_dict[col]/100.0 for col in unemployment_df.columns])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Saving" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Save some of the data processing constants for use within the environment" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "model_constants_dict = {}\n", + "\n", + "model_constants_dict[\"DATE_FORMAT\"] = DATE_FORMAT\n", + "model_constants_dict[\"STRINGENCY_POLICY_KEY\"] = STRINGENCY_POLICY_KEY\n", + "model_constants_dict[\"NUM_STRINGENCY_LEVELS\"] = int(NUM_STRINGENCY_LEVELS)\n", + "model_constants_dict[\"SIR_SMOOTHING_STD\"] = SIR_SMOOTHING_STD\n", + "model_constants_dict[\"SIR_MORTALITY\"] = SIR_MORTALITY\n", + "model_constants_dict[\"SIR_GAMMA\"] = SIR_GAMMA\n", + "model_constants_dict[\"US_STATE_IDX_TO_STATE_NAME\"] = {\n", + " us_state_idx: us_state for us_state_idx, us_state in enumerate(US_STATE_ORDER)\n", + "}\n", + "model_constants_dict[\"US_STATE_POPULATION\"] = [int(us_state_to_pop_dict[us_state]) for us_state in US_STATE_ORDER]\n", + "model_constants_dict[\"US_POPULATION\"] = int(sum([us_state_to_pop_dict[us_state] for us_state in US_STATE_ORDER]))\n", + "\n", + "# 2019: https://data.worldbank.org/indicator/NY.GDP.PCAP.CD?locations=US&view=chart\n", + "model_constants_dict[\"GDP_PER_CAPITA\"] = 65300 # TODO: Load this in from model_constants.json.\n", + "\n", + "model_constants_filename = \"model_constants.json\"\n", + "with open(os.path.join(data_dir, model_constants_filename), \"w\") as fp: \n", + " json.dump(model_constants_dict, fp)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Save all the processed dataframes in order to use for model fitting notebook (fit_model_parameters.ipynb)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "dataframes = {\n", + " \"policy\": policy_df,\n", + " \"subsidy\": subsidy_df,\n", + " \"deaths\": deaths_df,\n", + " \"vaccinated\": vaccinated_df,\n", + " \"smoothed_deaths\": smoothed_deaths_df,\n", + " \"susceptible\": susceptible_df,\n", + " \"infected\": infected_df,\n", + " \"recovered\": recovered_df,\n", + " \"beta\": beta_df,\n", + " \"unemployment\": unemployment_df,\n", + " \"unemployed\": unemployed_df,\n", + "}\n", + "\n", + "for k, df in dataframes.items():\n", + " if k == \"subsidy\": # This is at the USA level, not at the US states level\n", + " continue\n", + " assert (df.columns.to_list() == US_STATE_ORDER).all()\n", + "\n", + "with open(os.path.join(data_dir, 'dataframes.pkl'), 'wb') as F:\n", + " pickle.dump(dataframes, F)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Also save all the data as numpy arrays for use within the covid19 simulation environment" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "real_world_data = {}\n", + "for key in dataframes:\n", + " real_world_data[key] = dataframes[key].values\n", + " \n", + "# Save the real-world data as a .npz for use within the environment\n", + "np.savez(os.path.join(data_dir, \"real_world_data.npz\"), **real_world_data) " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Finally, in order to use this gathered real-world data when you run the covid19 simulation, you will need to also" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 1. Run the \"fit_model_parameters.ipynb\" notebook with the base data directory specified below." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(\"BASE_DATA_DIR_PATH = '{}'\".format(BASE_DATA_DIR_PATH))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 2. Set \"path_to_data_and_fitted_params\" in the env config also to the data directory below." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(\"path_to_data_and_fitted_params = '{}'\".format(data_dir))" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.4" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/ai_economist/datasets/covid19_datasets/us_deaths.py b/ai_economist/datasets/covid19_datasets/us_deaths.py new file mode 100644 index 0000000..7cb0a00 --- /dev/null +++ b/ai_economist/datasets/covid19_datasets/us_deaths.py @@ -0,0 +1,54 @@ +# Copyright (c) 2021, salesforce.com, inc. +# All rights reserved. +# SPDX-License-Identifier: BSD-3-Clause +# For full license text, see the LICENSE file in the repo root +# or https://opensource.org/licenses/BSD-3-Clause + +import os +from io import BytesIO + +import pandas as pd +import requests + + +class DatasetCovidDeathsUS: + """ + Class to load COVID-19 deaths data for the US. + Source: https://github.com/CSSEGISandData/COVID-19 + Note: in this dataset, reporting deaths only started on the 22nd of January 2020, + + Attributes: + df: Timeseries dataframe of confirmed COVID deaths for all the US states + """ + + def __init__(self, data_dir="", download_latest_data=True): + if not os.path.exists(data_dir): + print( + "Creating a dynamic data directory to store " + "COVID-19 deaths data: {}".format(data_dir) + ) + os.makedirs(data_dir) + + filename = "daily_us_deaths.csv" + if download_latest_data or filename not in os.listdir(data_dir): + print( + "Fetching latest U.S. COVID-19 deaths data from John Hopkins, " + "and saving it in {}".format(data_dir) + ) + + req = requests.get( + "https://raw.githubusercontent.com/CSSEGISandData/COVID-19/master/" + "csse_covid_19_data/csse_covid_19_time_series/" + "time_series_covid19_deaths_US.csv" + ) + self.df = pd.read_csv(BytesIO(req.content)) + self.df.to_csv( + os.path.join(data_dir, filename) + ) # Note: performs an overwrite + else: + print( + "Not fetching the latest U.S. COVID-19 deaths data from John Hopkins." + " Using whatever was saved earlier in {}!!".format(data_dir) + ) + assert filename in os.listdir(data_dir) + self.df = pd.read_csv(os.path.join(data_dir, filename), low_memory=False) diff --git a/ai_economist/datasets/covid19_datasets/us_policies.py b/ai_economist/datasets/covid19_datasets/us_policies.py new file mode 100644 index 0000000..486579c --- /dev/null +++ b/ai_economist/datasets/covid19_datasets/us_policies.py @@ -0,0 +1,122 @@ +# Copyright (c) 2021, salesforce.com, inc. +# All rights reserved. +# SPDX-License-Identifier: BSD-3-Clause +# For full license text, see the LICENSE file in the repo root +# or https://opensource.org/licenses/BSD-3-Clause + + +import os +from datetime import datetime +from io import BytesIO + +import numpy as np +import pandas as pd +import requests + + +class DatasetCovidPoliciesUS: + """ + Class to load COVID-19 government policies for the US states. + Source: https://github.com/OxCGRT/USA-covid-policy + + Other references: + - Codebook: https://github.com/OxCGRT/covid-policy-tracker/blob/master/ + documentation/codebook.md + - Index computation methodology: https://github.com/OxCGRT/covid-policy-tracker/ + blob/master/documentation/index_methodology.md + + Attributes: + df: Timeseries dataframe of state-wide policies + """ + + def __init__(self, data_dir="", download_latest_data=True): + if not os.path.exists(data_dir): + print( + "Creating a dynamic data directory to store COVID-19 " + "policy tracking data: {}".format(data_dir) + ) + os.makedirs(data_dir) + + filename = "daily_us_policies.csv" + if download_latest_data or filename not in os.listdir(data_dir): + print( + "Fetching latest U.S. COVID-19 policies data from OxCGRT, " + "and saving it in {}".format(data_dir) + ) + req = requests.get( + "https://raw.githubusercontent.com/OxCGRT/USA-covid-policy/master/" + "data/OxCGRT_US_latest.csv" + ) + self.df = pd.read_csv(BytesIO(req.content), low_memory=False) + self.df["Date"] = self.df["Date"].apply( + lambda x: datetime.strptime(str(x), "%Y%m%d") + ) + + # Fetch only the state-wide policies + self.df = self.df.loc[self.df["Jurisdiction"] != "NAT_GOV"] + + self.df.to_csv( + os.path.join(data_dir, filename) + ) # Note: performs an overwrite + else: + print( + "Not fetching the latest U.S. COVID-19 policies data from OxCGRT. " + "Using whatever was saved earlier in {}!!".format(data_dir) + ) + assert filename in os.listdir(data_dir) + self.df = pd.read_csv(os.path.join(data_dir, filename), low_memory=False) + + def process_policy_data( + self, stringency_policy_key="StringencyIndex", num_stringency_levels=10 + ): + """ + Gather the relevant policy indicator frm the dataframe, + fill in the null values (if any), + and discretize/quantize the policy into num_stringency_levels. + Note: Possible values for stringency_policy_key are + ["StringencyIndex", "Government response index", + "Containment and health index", "Economic Support index".] + Reference: https://github.com/OxCGRT/covid-policy-tracker/blob/master/ + documentation/index_methodology.md + """ + + def discretize(policies, num_indicator_levels=10): + """ + Discretize the policies (a Pandas series) into num_indicator_levels + """ + # Indices are normalized to be in [0, 100] + bins = np.linspace(0, 100, num_indicator_levels) + # Find left and right values of bin and find the nearer edge + bin_index = np.digitize(policies, bins, right=True) + bin_left_edges = bins[bin_index - 1] + bin_right_edges = bins[bin_index] + discretized_policies = bin_index + np.argmin( + np.stack( + ( + np.abs(policies.values - bin_left_edges), + np.abs(policies.values - bin_right_edges), + ) + ), + axis=0, + ) + return discretized_policies + + # Gather just the relevant columns + policy_df = self.df[["RegionName", "Date", stringency_policy_key]].copy() + + # Fill in null values via a "forward fill" + policy_df[stringency_policy_key].fillna(method="ffill", inplace=True) + + # Discretize the stringency indices + discretized_stringency_policies = discretize( + policy_df[stringency_policy_key], num_indicator_levels=num_stringency_levels + ) + policy_df.loc[:, stringency_policy_key] = discretized_stringency_policies + + # Replace Washington DC by District of Columbia to keep consistent + # (with the other data sources) + policy_df = policy_df.replace("Washington DC", "District of Columbia") + + policy_df = policy_df.sort_values(by=["RegionName", "Date"]) + + return policy_df diff --git a/ai_economist/datasets/covid19_datasets/us_unemployment.py b/ai_economist/datasets/covid19_datasets/us_unemployment.py new file mode 100644 index 0000000..6552f61 --- /dev/null +++ b/ai_economist/datasets/covid19_datasets/us_unemployment.py @@ -0,0 +1,128 @@ +# Copyright (c) 2021, salesforce.com, inc. +# All rights reserved. +# SPDX-License-Identifier: BSD-3-Clause +# For full license text, see the LICENSE file in the repo root +# or https://opensource.org/licenses/BSD-3-Clause + +import bz2 +import os +import pickle +import queue +import threading +import urllib.request as urllib2 + +import pandas as pd +from bs4 import BeautifulSoup + + +class DatasetCovidUnemploymentUS: + """ + Class to load COVID-19 unemployment data for the US states. + Source: https://www.bls.gov/lau/ + """ + + def __init__(self, data_dir="", download_latest_data=True): + if not os.path.exists(data_dir): + print( + "Creating a dynamic data directory to store COVID-19 " + "unemployment data: {}".format(data_dir) + ) + os.makedirs(data_dir) + + filename = "monthly_us_unemployment.bz2" + if download_latest_data or filename not in os.listdir(data_dir): + # Construct the U.S. state to FIPS code mapping + state_fips_df = pd.read_excel( + "https://www2.census.gov/programs-surveys/popest/geographies/2017/" + "state-geocodes-v2017.xlsx", + header=5, + ) + # remove all statistical areas and cities + state_fips_df = state_fips_df.loc[state_fips_df["State (FIPS)"] != 0] + self.us_state_to_fips_dict = pd.Series( + state_fips_df["State (FIPS)"].values, index=state_fips_df.Name + ).to_dict() + + print( + "Fetching the U.S. unemployment data from " + "Bureau of Labor and Statistics, and saving it in {}".format(data_dir) + ) + self.data = self.scrape_bls_data() + fp = bz2.BZ2File(os.path.join(data_dir, filename), "wb") + pickle.dump(self.data, fp) + fp.close() + + else: + print( + "Not fetching the U.S. unemployment data from Bureau of Labor and" + " Statistics. Using whatever was saved earlier in {}!!".format(data_dir) + ) + assert filename in os.listdir(data_dir) + with bz2.BZ2File(os.path.join(data_dir, filename), "rb") as fp: + self.data = pickle.load(fp) + fp.close() + + # Scrape monthly unemployment from the Bureau of Labor Statistics website + def get_monthly_bls_unemployment_rates(self, state_fips): + with urllib2.urlopen( + "https://data.bls.gov/timeseries/LASST{:02d}0000000000003".format( + state_fips + ) + ) as response: + html_doc = response.read() + + soup = BeautifulSoup(html_doc, "html.parser") + table = soup.find_all("table")[1] + table_rows = table.find_all("tr") + + unemployment_dict = {} + + mth2idx = { + "Jan": 1, + "Feb": 2, + "Mar": 3, + "Apr": 4, + "May": 5, + "Jun": 6, + "Jul": 7, + "Aug": 8, + "Sep": 9, + "Oct": 10, + "Nov": 11, + "Dec": 12, + } + + for tr in table_rows[1:-1]: + td = tr.find_all("td")[-1] + unemp = float("".join([c for c in td.text if c.isdigit() or c == "."])) + th = tr.find_all("th") + year = int(th[0].text) + month = mth2idx[th[1].text] + if year not in unemployment_dict: + unemployment_dict[year] = {} + unemployment_dict[year][month] = unemp + + return unemployment_dict + + def scrape_bls_data(self): + def do_scrape(us_state, fips, queue_obj): + out = self.get_monthly_bls_unemployment_rates(fips) + queue_obj.put([us_state, out]) + + print("Getting BLS Data. This might take a minute...") + result = queue.Queue() + threads = [ + threading.Thread(target=do_scrape, args=(us_state, fips, result)) + for us_state, fips in self.us_state_to_fips_dict.items() + ] + for t in threads: + t.start() + for t in threads: + t.join() + + monthly_unemployment = {} + while not result.empty(): + us_state, data = result.get() + monthly_unemployment[us_state] = data + + return monthly_unemployment diff --git a/ai_economist/datasets/covid19_datasets/us_vaccinations.py b/ai_economist/datasets/covid19_datasets/us_vaccinations.py new file mode 100644 index 0000000..9e21be2 --- /dev/null +++ b/ai_economist/datasets/covid19_datasets/us_vaccinations.py @@ -0,0 +1,61 @@ +# Copyright (c) 2021, salesforce.com, inc. +# All rights reserved. +# SPDX-License-Identifier: BSD-3-Clause +# For full license text, see the LICENSE file in the repo root +# or https://opensource.org/licenses/BSD-3-Clause + +import os +from io import BytesIO + +import pandas as pd +import requests + + +class DatasetCovidVaccinationsUS: + """ + Class to load COVID-19 vaccination data for the US. + Source: https://ourworldindata.org/covid-vaccinations + + Attributes: + df: Timeseries dataframe of COVID vaccinations for all the US states + """ + + def __init__(self, data_dir="", download_latest_data=True): + if not os.path.exists(data_dir): + print( + "Creating a dynamic data directory to store COVID-19 " + "vaccination data: {}".format(data_dir) + ) + os.makedirs(data_dir) + + filename = "daily_us_vaccinations.csv" + if download_latest_data or filename not in os.listdir(data_dir): + print( + "Fetching latest U.S. COVID-19 vaccination data from " + "Our World in Data, and saving it in {}".format(data_dir) + ) + + req = requests.get( + "https://raw.githubusercontent.com/owid/covid-19-data/master/" + "public/data/vaccinations/us_state_vaccinations.csv" + ) + self.df = pd.read_csv(BytesIO(req.content)) + + # Rename New York State to New York for consistency with other datasets + self.df = self.df.replace("New York State", "New York") + + # Interpolate missing values + self.df = self.df.interpolate(method="linear") + + self.df.to_csv( + os.path.join(data_dir, filename) + ) # Note: performs an overwrite + else: + print( + "Not fetching the latest U.S. COVID-19 deaths data from " + "Our World in Data. Using whatever was saved earlier in {}!!".format( + data_dir + ) + ) + assert filename in os.listdir(data_dir) + self.df = pd.read_csv(os.path.join(data_dir, filename), low_memory=False) diff --git a/ai_economist/foundation/__init__.py b/ai_economist/foundation/__init__.py new file mode 100644 index 0000000..d92cf27 --- /dev/null +++ b/ai_economist/foundation/__init__.py @@ -0,0 +1,18 @@ +# Copyright (c) 2020, salesforce.com, inc. +# All rights reserved. +# SPDX-License-Identifier: BSD-3-Clause +# For full license text, see the LICENSE file in the repo root +# or https://opensource.org/licenses/BSD-3-Clause + +from ai_economist.foundation import utils +from ai_economist.foundation.agents import agent_registry as agents +from ai_economist.foundation.components import component_registry as components +from ai_economist.foundation.entities import endogenous_registry as endogenous +from ai_economist.foundation.entities import landmark_registry as landmarks +from ai_economist.foundation.entities import resource_registry as resources +from ai_economist.foundation.scenarios import scenario_registry as scenarios + + +def make_env_instance(scenario_name, **kwargs): + scenario_class = scenarios.get(scenario_name) + return scenario_class(**kwargs) diff --git a/ai_economist/foundation/agents/__init__.py b/ai_economist/foundation/agents/__init__.py new file mode 100644 index 0000000..67df723 --- /dev/null +++ b/ai_economist/foundation/agents/__init__.py @@ -0,0 +1,12 @@ +# Copyright (c) 2020, salesforce.com, inc. +# All rights reserved. +# SPDX-License-Identifier: BSD-3-Clause +# For full license text, see the LICENSE file in the repo root +# or https://opensource.org/licenses/BSD-3-Clause + +from ai_economist.foundation.base.base_agent import agent_registry + +from . import mobiles, planners + +# Import files that add Agent class(es) to agent_registry +# ------------------------------------------------------- diff --git a/ai_economist/foundation/agents/mobiles.py b/ai_economist/foundation/agents/mobiles.py new file mode 100644 index 0000000..b9f0b8c --- /dev/null +++ b/ai_economist/foundation/agents/mobiles.py @@ -0,0 +1,18 @@ +# Copyright (c) 2020, salesforce.com, inc. +# All rights reserved. +# SPDX-License-Identifier: BSD-3-Clause +# For full license text, see the LICENSE file in the repo root +# or https://opensource.org/licenses/BSD-3-Clause + +from ai_economist.foundation.base.base_agent import BaseAgent, agent_registry + + +@agent_registry.add +class BasicMobileAgent(BaseAgent): + """ + A basic mobile agent represents an individual actor in the economic simulation. + + "Mobile" refers to agents of this type being able to move around in the 2D world. + """ + + name = "BasicMobileAgent" diff --git a/ai_economist/foundation/agents/planners.py b/ai_economist/foundation/agents/planners.py new file mode 100644 index 0000000..7f555fb --- /dev/null +++ b/ai_economist/foundation/agents/planners.py @@ -0,0 +1,40 @@ +# Copyright (c) 2020, salesforce.com, inc. +# All rights reserved. +# SPDX-License-Identifier: BSD-3-Clause +# For full license text, see the LICENSE file in the repo root +# or https://opensource.org/licenses/BSD-3-Clause + +from ai_economist.foundation.base.base_agent import BaseAgent, agent_registry + + +@agent_registry.add +class BasicPlanner(BaseAgent): + """ + A basic planner agent represents a social planner that sets macroeconomic policy. + + Unlike the "mobile" agent, the planner does not represent an embodied agent in + the world environment. BasicPlanner modifies the BaseAgent class to remove + location as part of the agent state. + + Also unlike the "mobile" agent, the planner agent is expected to be unique -- + that is, there should only be 1 planner. For this reason, BasicPlanner ignores + the idx argument during construction and always sets its agent index as "p". + """ + + name = "BasicPlanner" + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + del self.state["loc"] + + # Overwrite any specified index so that this one is always indexed as 'p' + # (make a separate class of planner if you want there to be multiple planners + # in a game) + self._idx = "p" + + @property + def loc(self): + """ + Planner agents do not occupy any location. + """ + raise AttributeError("BasicPlanner agents do not occupy a location.") diff --git a/ai_economist/foundation/base/__init__.py b/ai_economist/foundation/base/__init__.py new file mode 100644 index 0000000..fc6cee4 --- /dev/null +++ b/ai_economist/foundation/base/__init__.py @@ -0,0 +1,5 @@ +# Copyright (c) 2020, salesforce.com, inc. +# All rights reserved. +# SPDX-License-Identifier: BSD-3-Clause +# For full license text, see the LICENSE file in the repo root +# or https://opensource.org/licenses/BSD-3-Clause diff --git a/ai_economist/foundation/base/base_agent.py b/ai_economist/foundation/base/base_agent.py new file mode 100644 index 0000000..b5a627c --- /dev/null +++ b/ai_economist/foundation/base/base_agent.py @@ -0,0 +1,490 @@ +# Copyright (c) 2020, salesforce.com, inc. +# All rights reserved. +# SPDX-License-Identifier: BSD-3-Clause +# For full license text, see the LICENSE file in the repo root +# or https://opensource.org/licenses/BSD-3-Clause + +import random + +import numpy as np + +from ai_economist.foundation.base.registrar import Registry + + +class BaseAgent: + """Base class for Agent classes. + + Instances of Agent classes are created for each agent in the environment. Agent + instances are stateful, capturing location, inventory, endogenous variables, + and any additional state fields created by environment components during + construction (see BaseComponent.get_additional_state_fields in base_component.py). + + They also provide a simple API for getting/setting actions for each of their + registered action subspaces (which depend on the components used to build + the environment). + + Args: + idx (int or str): Index that uniquely identifies the agent object amongst the + other agent objects registered in its environment. + multi_action_mode (bool): Whether to allow the agent to take one action for + each of its registered action subspaces each timestep (if True), + or to limit the agent to take only one action each timestep (if False). + """ + + name = "" + + def __init__(self, idx=None, multi_action_mode=None): + assert self.name + + if idx is None: + idx = 0 + + if multi_action_mode is None: + multi_action_mode = False + + if isinstance(idx, str): + self._idx = idx + else: + self._idx = int(idx) + + self.multi_action_mode = bool(multi_action_mode) + self.single_action_map = ( + {} + ) # Used to convert single-action-mode actions to the general format + + self.action = dict() + self.action_dim = dict() + self._action_names = [] + self._multi_action_dict = {} + self._unique_actions = 0 + self._total_actions = 0 + + self.state = dict(loc=[0, 0], inventory={}, escrow={}, endogenous={}) + + self._registered_inventory = False + self._registered_endogenous = False + self._registered_components = False + self._noop_action_dict = dict() + + # Special flag to allow logic for multi-action-mode agents + # that are not given any actions. + self._passive_multi_action_agent = False + + # If this gets set to true, we can make masks faster + self._one_component_single_action = False + self._premask = None + + @property + def idx(self): + """Index used to identify this agent. Must be unique within the environment.""" + return self._idx + + def register_inventory(self, resources): + """Used during environment construction to populate inventory/escrow fields.""" + assert not self._registered_inventory + for entity_name in resources: + self.inventory[entity_name] = 0 + self.escrow[entity_name] = 0 + self._registered_inventory = True + + def register_endogenous(self, endogenous): + """Used during environment construction to populate endogenous state fields.""" + assert not self._registered_endogenous + for entity_name in endogenous: + self.endogenous[entity_name] = 0 + self._registered_endogenous = True + + def _incorporate_component(self, action_name, n): + extra_n = ( + 1 if self.multi_action_mode else 0 + ) # Each sub-action has a NO-OP in multi action mode) + self.action[action_name] = 0 + self.action_dim[action_name] = n + extra_n + self._action_names.append(action_name) + self._multi_action_dict[action_name] = False + self._unique_actions += 1 + if self.multi_action_mode: + self._total_actions += n + extra_n + else: + for action_n in range(1, n + 1): + self._total_actions += 1 + self.single_action_map[int(self._total_actions)] = [ + action_name, + action_n, + ] + + def register_components(self, components): + """Used during environment construction to set up state/action spaces.""" + assert not self._registered_components + for component in components: + n = component.get_n_actions(self.name) + if n is None: + continue + + # Most components will have a single action-per-agent, so n is an int + if isinstance(n, int): + if n == 0: + continue + self._incorporate_component(component.name, n) + + # They can also internally handle multiple actions-per-agent, + # so n is an tuple or list + elif isinstance(n, (tuple, list)): + for action_sub_name, n_ in n: + if n_ == 0: + continue + if "." in action_sub_name: + raise NameError( + "Sub-action {} of component {} " + "is illegally named.".format( + action_sub_name, component.name + ) + ) + self._incorporate_component( + "{}.{}".format(component.name, action_sub_name), n_ + ) + + # If that's not what we got something is funky. + else: + raise TypeError( + "Received unexpected type ({}) from {}.get_n_actions('{}')".format( + type(n), component.name, self.name + ) + ) + + for k, v in component.get_additional_state_fields(self.name).items(): + self.state[k] = v + + # Currently no actions are available to this agent. Give it a placeholder. + if len(self.action) == 0 and self.multi_action_mode: + self._incorporate_component("PassiveAgentPlaceholder", 0) + self._passive_multi_action_agent = True + + elif len(self.action) == 1 and not self.multi_action_mode: + self._one_component_single_action = True + self._premask = np.ones(1 + self._total_actions, dtype=np.float32) + + self._registered_components = True + + self._noop_action_dict = {k: v * 0 for k, v in self.action.items()} + + verbose = False + if verbose: + print(self.name, self.idx, "constructed action map:") + for k, v in self.single_action_map.items(): + print("single action map:", k, v) + for k, v in self.action.items(): + print("action:", k, v) + for k, v in self.action_dim.items(): + print("action_dim:", k, v) + + @property + def action_spaces(self): + """ + if self.multi_action_mode == True: + Returns an integer array with length equal to the number of action + subspaces that the agent registered. The i'th element of the array + indicates the number of actions associated with the i'th action subspace. + In multi_action_mode, each subspace includes a NO-OP. + Note: self._action_names describes which action subspace each element of + the array refers to. + + Example: + >> self.multi_action_mode + True + >> self.action_spaces + [2, 5] + >> self._action_names + ["Build", "Gather"] + # [1 Build action + Build NO-OP, 4 Gather actions + Gather NO-OP] + + if self.multi_action_mode == False: + Returns a single integer equal to the total number of actions that the + agent can take. + + Example: + >> self.multi_action_mode + False + >> self.action_spaces + 6 + >> self._action_names + ["Build", "Gather"] + # 1 NO-OP + 1 Build action + 4 Gather actions. + """ + if self.multi_action_mode: + action_dims = [] + for m in self._action_names: + action_dims.append(np.array(self.action_dim[m]).reshape(-1)) + return np.concatenate(action_dims).astype(np.int32) + n_actions = 1 # (NO-OP) + for m in self._action_names: + n_actions += self.action_dim[m] + return n_actions + + @property + def loc(self): + """2D list of [row, col] representing agent's location in the environment.""" + return self.state["loc"] + + @property + def endogenous(self): + """Dictionary representing endogenous quantities (i.e. "Labor"). + + Example: + >> self.endogenous + {"Labor": 30.25} + """ + return self.state["endogenous"] + + @property + def inventory(self): + """Dictionary representing quantities of resources in agent's inventory. + + Example: + >> self.inventory + {"Wood": 3, "Stone": 20, "Coin": 1002.83} + """ + return self.state["inventory"] + + @property + def escrow(self): + """Dictionary representing quantities of resources in agent's escrow. + + https://en.wikipedia.org/wiki/Escrow + Escrow is used to manage any portion of the agent's inventory that is + reserved for a particular purpose. Typically, something enters escrow as part + of a contractual arrangement to disburse that something when another + condition is met. An example is found in the ContinuousDoubleAuction + Component class (see ../components/continuous_double_auction.py). When an + agent creates an order to sell a unit of Wood, for example, the component + moves one unit of Wood from the agent's inventory to its escrow. If another + agent buys the Wood, it is moved from escrow to the other agent's inventory. By + placing the Wood in escrow, it prevents the first agent from using it for + something else (i.e. building a house). + + Notes: + The inventory and escrow share the same keys. An agent's endowment refers + to the total quantity it has in its inventory and escrow. + + Escrow is provided to simplify inventory management but its intended + semantics are not enforced directly. It is up to Component classes to + enforce these semantics. + + Example: + >> self.inventory + {"Wood": 0, "Stone": 1, "Coin": 3} + """ + return self.state["escrow"] + + def inventory_to_escrow(self, resource, amount): + """Move some amount of a resource from agent inventory to agent escrow. + + Amount transferred is capped to the amount of resource in agent inventory. + + Args: + resource (str): The name of the resource to move (i.e. "Wood", "Coin"). + amount (float): The amount to be moved from inventory to escrow. Must be + positive. + + Returns: + Amount of resource actually transferred. Will be less than amount argument + if amount argument exceeded the amount of resource in the inventory. + Calculated as: + transferred = np.minimum(self.state["inventory"][resource], amount) + """ + assert amount >= 0 + transferred = float(np.minimum(self.state["inventory"][resource], amount)) + self.state["inventory"][resource] -= transferred + self.state["escrow"][resource] += transferred + return float(transferred) + + def escrow_to_inventory(self, resource, amount): + """Move some amount of a resource from agent escrow to agent inventory. + + Amount transferred is capped to the amount of resource in agent escrow. + + Args: + resource (str): The name of the resource to move (i.e. "Wood", "Coin"). + amount (float): The amount to be moved from escrow to inventory. Must be + positive. + + Returns: + Amount of resource actually transferred. Will be less than amount argument + if amount argument exceeded the amount of resource in escrow. + Calculated as: + transferred = np.minimum(self.state["escrow"][resource], amount) + """ + assert amount >= 0 + transferred = float(np.minimum(self.state["escrow"][resource], amount)) + self.state["escrow"][resource] -= transferred + self.state["inventory"][resource] += transferred + return float(transferred) + + def total_endowment(self, resource): + """Get the combined inventory+escrow endowment of resource. + + Args: + resource (str): Name of the resource + + Returns: + The amount of resource in the agents inventory and escrow. + + """ + return self.inventory[resource] + self.escrow[resource] + + def reset_actions(self, component=None): + """Reset all actions to the NO-OP action (the 0'th action index). + + If component is specified, only reset action(s) for that component. + """ + if not component: + self.action.update(self._noop_action_dict) + else: + for k, v in self.action.items(): + if "." in component: + if k.lower() == component.lower(): + self.action[k] = v * 0 + else: + base_component = k.split(".")[0] + if base_component.lower() == component.lower(): + self.action[k] = v * 0 + + def has_component(self, component_name): + """Returns True if the agent has component_name as a registered subaction.""" + return bool(component_name in self.action) + + def get_random_action(self): + """ + Select a component at random and randomly choose one of its actions (other + than NO-OP). + """ + random_component = random.choice(self._action_names) + component_action = random.choice( + list(range(1, self.action_dim[random_component])) + ) + return {random_component: component_action} + + def get_component_action(self, component_name, sub_action_name=None): + """ + Return the action(s) taken for component_name component, or None if the + agent does not use that component. + """ + if sub_action_name is not None: + return self.action.get(component_name + "." + sub_action_name, None) + matching_names = [ + m for m in self._action_names if m.split(".")[0] == component_name + ] + if len(matching_names) == 0: + return None + if len(matching_names) == 1: + return self.action.get(matching_names[0], None) + return [self.action.get(m, None) for m in matching_names] + + def set_component_action(self, component_name, action): + """Set the action(s) taken for component_name component.""" + if component_name not in self.action: + raise KeyError( + "Agent {} of type {} does not have {} registered as a subaction".format( + self.idx, self.name, component_name + ) + ) + if self._multi_action_dict[component_name]: + self.action[component_name] = np.array(action, dtype=np.int32) + else: + self.action[component_name] = int(action) + + def populate_random_actions(self): + """Fill the action buffer with random actions. This is for testing.""" + for component, d in self.action_dim.items(): + if isinstance(d, int): + self.set_component_action(component, np.random.randint(0, d)) + else: + d_array = np.array(d) + self.set_component_action( + component, np.floor(np.random.rand(*d_array.shape) * d_array) + ) + + def parse_actions(self, actions): + """Parse the actions array to fill each component's action buffers.""" + if self.multi_action_mode: + assert len(actions) == self._unique_actions + if len(actions) == 1: + self.set_component_action(self._action_names[0], actions[0]) + else: + for action_name, action in zip(self._action_names, actions): + self.set_component_action(action_name, int(action)) + + # Single action mode + else: + # Action was supplied as an index of a specific subaction. + # No need to do any lookup. + if isinstance(actions, dict): + if len(actions) == 0: + return + assert len(actions) == 1 + action_name = list(actions.keys())[0] + action = list(actions.values())[0] + if action == 0: + return + self.set_component_action(action_name, action) + + # Action was supplied as an index into the full set of combined actions + else: + action = int(actions) + # Universal NO-OP + if action == 0: + return + action_name, action = self.single_action_map.get(action) + self.set_component_action(action_name, action) + + def flatten_masks(self, mask_dict): + """Convert a dictionary of component action masks into a single mask vector.""" + if self._one_component_single_action: + self._premask[1:] = mask_dict[self._action_names[0]] + return self._premask + + no_op_mask = [1] + + if self._passive_multi_action_agent: + return np.array(no_op_mask).astype(np.float32) + + list_of_masks = [] + if not self.multi_action_mode: + list_of_masks.append(no_op_mask) + for m in self._action_names: + if m not in mask_dict: + raise KeyError("No mask provided for {} (agent {})".format(m, self.idx)) + if self.multi_action_mode: + list_of_masks.append(no_op_mask) + list_of_masks.append(mask_dict[m]) + return np.concatenate(list_of_masks).astype(np.float32) + + +agent_registry = Registry(BaseAgent) +"""The registry for Agent classes. + +This creates a registry object for Agent classes. This registry requires that all +added classes are subclasses of BaseAgent. To make an Agent class available through +the registry, decorate the class definition with @agent_registry.add. + +Example: + from ai_economist.foundation.base.base_agent import BaseAgent, agent_registry + + @agent_registry.add + class ExampleAgent(BaseAgent): + name = "Example" + pass + + assert agent_registry.has("Example") + + AgentClass = agent_registry.get("Example") + agent = AgentClass(...) + assert isinstance(agent, ExampleAgent) + +Notes: + The foundation package exposes the agent registry as: foundation.agents + + An Agent class that is defined and registered following the above example will + only be visible in foundation.agents if defined/registered in a file that is + imported in ../agents/__init__.py. +""" diff --git a/ai_economist/foundation/base/base_component.py b/ai_economist/foundation/base/base_component.py new file mode 100644 index 0000000..aea66b8 --- /dev/null +++ b/ai_economist/foundation/base/base_component.py @@ -0,0 +1,406 @@ +# Copyright (c) 2020, salesforce.com, inc. +# All rights reserved. +# SPDX-License-Identifier: BSD-3-Clause +# For full license text, see the LICENSE file in the repo root +# or https://opensource.org/licenses/BSD-3-Clause + +from abc import ABC, abstractmethod + +import numpy as np + +from ai_economist.foundation.agents import agent_registry +from ai_economist.foundation.base.registrar import Registry +from ai_economist.foundation.base.world import World + + +class BaseComponent(ABC): + """ + Base Component class. Should be used as the parent class for Component classes. + Component instances are used to add some particular dynamics to an environment. + They also add action spaces through which agents can interact with the + environment via the component instance. + + Environments expand the agents' state/action spaces by querying: + get_n_actions + get_additional_state_fields + + Environments expand their dynamics by querying: + component_step + generate_observations + generate_masks + + Environments expand logging behavior by querying: + get_metrics + get_dense_log + + Because they are built as Python objects, component instances can also be + stateful. Stateful attributes are reset via calls to: + additional_reset_steps + + The semantics of each method, and how they can be used to construct an instance + of the Component class, are detailed below. + + Refer to ../components/move.py for an example of a Component class that enables + mobile agents to move and collect resources in the environment world. + """ + + # The name associated with this Component class (must be unique). + # Note: This is what will identify the Component class in the component registry. + name = "" + + # An optional shorthand description of the what the component implements (i.e. + # "Trading", "Building", etc.). See BaseEnvironment.get_component and + # BaseEnvironment._finalize_logs to see where this may add convenience. + # Does not need to be unique. + component_type = None + + # The (sub)classes of agents that this component applies to + agent_subclasses = None # Replace with list or tuple (can be empty) + + # The (non-agent) game entities that are expected to be in play + required_entities = None # Replace with list or tuple (can be empty) + + def __init__(self, world, episode_length, inventory_scale=1): + assert self.name + + assert isinstance(self.agent_subclasses, (tuple, list)) + assert len(self.agent_subclasses) > 0 + if len(self.agent_subclasses) > 1: + for i in range(len(self.agent_subclasses)): + for j in range(len(self.agent_subclasses)): + if i == j: + continue + a_i = agent_registry.get(self.agent_subclasses[i]) + a_j = agent_registry.get(self.agent_subclasses[j]) + assert not issubclass(a_i, a_j) + + assert isinstance(self.required_entities, (tuple, list)) + + self.check_world(world) + self._world = world + + assert isinstance(episode_length, int) and episode_length > 0 + self._episode_length = episode_length + + self.n_agents = world.n_agents + self.resources = world.resources + self.landmarks = world.landmarks + + self.timescale = 1 + assert self.timescale >= 1 + + self._inventory_scale = float(inventory_scale) + + @property + def world(self): + """The world object of the environment this component instance is part of. + + The world object exposes the spatial/agent states through: + world.maps # Reference to maps object representing spatial state + world.agents # List of self.n_agents mobile agent objects + world.planner # Reference to planner agent object + + See world.py and base_agent.py for additional API details. + """ + return self._world + + @property + def episode_length(self): + """Episode length of the environment this component instance is a part of.""" + return int(self._episode_length) + + @property + def inv_scale(self): + """ + Value by which to scale quantities when generating observations. + + Note: This property is set by the environment during construction and + allows each component instance within the environment to refer to the same + scaling value. How the value is actually used depends on the implementation + of get_observations(). + """ + return self._inventory_scale + + @property + def shorthand(self): + """The shorthand name, or name if no component_type is defined.""" + return self.name if self.component_type is None else self.component_type + + @staticmethod + def check_world(world): + """Validate the world object.""" + assert isinstance(world, World) + + def reset(self): + """Reset any portion of the state managed by this component.""" + world = self.world + all_agents = world.agents + [world.planner] + for agent in all_agents: + agent.state.update(self.get_additional_state_fields(agent.name)) + + # This method allows components to define additional reset steps + self.additional_reset_steps() + + def obs(self): + """ + Observation produced by this component, given current world/agents/component + state. + """ + # This is mostly just to ensure formatting. + obs = self.generate_observations() + assert isinstance(obs, dict) + obs = {str(k): v for k, v in obs.items()} + return obs + + # Required methods for implementing components + # -------------------------------------------- + + @abstractmethod + def get_n_actions(self, agent_cls_name): + """ + Return the number of actions (not including NO-OPs) for agents of type + agent_cls_name. + + Args: + agent_cls_name (str): name of the Agent class for which number of actions + is being queried. For example, "BasicMobileAgent". + + Returns: + action_space (None, int, or list): If the component does not add any + actions for agents of type agent_cls_name, return None. If it adds a + single action space, return an integer specifying the number of + actions in the action space. If it adds multiple action spaces, + return a list of tuples ("action_set_name", num_actions_in_set). + See below for further detail. + + If agent_class_name type agents do not participate in the component, simply + return None + + In the next simplest case, the component adds one set of n different actions + for agents of type agent_cls_name. In this case, return n (as an int). For + example, if Component implements moving up, down, left, or right for + "BasicMobileAgent" agents, then Component.get_n_actions('Mobile') should + return 4. + + If the component adds multiple sets of actions for a given agent type, this + method should return a list of tuples: + [("action_set_name_1", n_1), ..., ("action_set_name_M", n_M)], + where M is the number of different sets of actions, and n_k is the number of + actions in action set k. + For example, if Component allows agent 'Planner' to set some tax for each of + individual Mobile agents, and there are 3 such agents, then: + Component.get_n_actions('Planner') should return, i.e., + [('Tax_0', 10), ('Tax_1', 10), ('Tax_2', 10)], + where, in this example, the Planner agent can choose 10 different tax + levels for each Mobile agent. + """ + + @abstractmethod + def get_additional_state_fields(self, agent_cls_name): + """ + Return a dictionary of {state_field: reset_val} managed by this Component + class for agents of type agent_cls_name. This also partially controls reset + behavior. + + Args: + agent_cls_name (str): name of the Agent class for which additional states + are being queried. For example, "BasicMobileAgent". + + Returns: + extra_state_dict (dict): A dictionary of {"state_field": reset_val} for + each extra state field that this component adds/manages to agents of + type agent_cls_name. This extra_state_dict is incorporated into + agent.state for each agent of this type. Note that the keyed fields + will be reset to reset_val when the environment is reset. + + If the component has its own internal state, the protocol for resetting that + should be written into the custom method 'additional_reset_steps()' [see below]. + + States that are meant to be internal to the component do not need to be + registered as agent state fields. Rather, adding to the agent state fields is + most useful when two or more components refer to or affect the same state. In + general, however, if the component expects a particular state field to exist, + it should use return that field (and its reset value) here. + """ + + @abstractmethod + def component_step(self): + """ + For all relevant agents, execute the actions specific to this Component class. + This is essentially where the component logic is implemented and what allows + components to create environment dynamics. + + If the component expects certain resources/landmarks/entities to be in play, + it must declare them in 'required_entities' so that they can be registered as + part of the world and, where appropriate, part of the agent inventory. + + If the component expects non-standard fields to exist in agent.state for one + or more agent types, that must be reflected in get_additional_state_fields(). + """ + + @abstractmethod + def generate_observations(self): + """ + Generate observations associated with this Component class. + + A component does not need to produce observations and can provide observations + for only some agent types; however, for a given environment, the structure of + the observations returned by this component should be identical between + subsequent calls to generate_observations. That is, the agents that receive + observations should remain consistent as should the structure of their + individual observations. + + Returns: + obs (dict): A dictionary of {agent.idx: agent_obs_dict}. In words, + return a dictionary with an entry for each agent (which can include + the planner) for which this component provides an observation. For each + entry, the key specifies the index of the agent and the value contains + its associated observation dictionary. + """ + + @abstractmethod + def generate_masks(self, completions=0): + """ + Create action masks to indicate which actions are and are not valid. Actions + that are valid should be given a value of 1 and 0 otherwise. Do not generate + a mask for the NO-OP action, which is always available. + + Args: + completions (int): The number of completed episodes. This is intended to + be used in the case that actions may be masked or unmasked as part of a + learning curriculum. + + Returns: + masks (dict): A dictionary of {agent.idx: mask} with an entry for each + agent that can interact with this component. See below. + + + The expected output parallels the action subspaces defined by get_n_actions(): + The output should be a dictionary of {agent.idx: mask} keyed for all agents + that take actions via this component. + + For example, say the component defines a set of 4 actions for agents of type + "BasicMobileAgent" (self.get_n_actions("BasicMobileAgent) --> 4). Because all + action spaces include a NO-OP action, there are 5 available actions, + interpreted in this example as: NO-OP (index=0), moving up (index=1), + down (index=2), left (index=3), or right (index=4). Say also that agent-0 (the + agent with agent.idx=0) is prevented from moving left but can otherwise move. + In this case, generate_masks(world)['0'] should point to a length-4 binary + array, specifically [1, 1, 0, 1]. Note that the mask is length 4 while + technically 5 actions are available. This is because NO-OP should be ignored + when constructing masks. + + In the more complex case where the component defines several action sets for + an agent, say the planner agent (the agent with agent.idx='p'), then + generate_masks(world)['p'] should point to a dictionary of + {"action_set_name_m": mask_m} for each of the M action sets associated with + agent p's type. Each such value, mask_m, should be a binary array whose + length matches the number of actions in "action_set_name_m". + + The default behavior (below) keeps all actions available. The code gives an + example of expected formatting. + """ + world = self.world + masks = {} + # For all the agents in the environment + for agent in world.agents + [world.planner]: + # Get any action space(s) defined by this component for this agent + n_actions = self.get_n_actions(agent.name) + + # If no action spaces are defined, just move on. + if n_actions is None: + continue + + # If a single action space is defined, n_actions corresponds to the + # number of (non NO-OP) actions. Return an array of ones of that length, + # enabling all actions. + if isinstance(n_actions, (int, float)): + masks[agent.idx] = np.ones(int(n_actions)) + + # If multiple action spaces are defined, n_actions corresponds to the + # tuple or list giving ("name", N) for each action space, where "name" + # is the unique name and N is the number of (non NO-OP) actions + # associated with that action space. + # Return a dictionary of {"name": length-N ones array}, enabling all + # actions in all the action spaces. + elif isinstance(n_actions, (tuple, list)): + masks[agent.idx] = { + sub_name: np.ones(int(sub_n)) for sub_name, sub_n in n_actions + } + + else: + raise TypeError + + return masks + + # For non-required customization + # ------------------------------ + + def additional_reset_steps(self): + """ + Use this method to implement additional steps that the component should + perform at reset. Useful for resetting internal trackers. + + This method should not return anything. + """ + return + + def get_metrics(self): + """ + Returns a dictionary of custom metrics describing the episode through the + lens of the component. + + For example, if Build is a subclass of BaseComponent that implements building, + Build.get_metrics() might return a dictionary with terms relating to the + number of things each agent built. + + Returns: + metrics (dict or None): A dictionary of {"metric_key": metric_value} + entries describing the metrics that this component calculates. The + environment combines scenario metrics with each of the metric + dictionaries produced by its component instances. metric_value is + expected to be a scalar. + By returning None instead of a dictionary, the component is ignored + by the environment when constructing the full metric report. + """ + return None + + def get_dense_log(self): + """ + Return the dense log, either a tuple, list, or dict, of the episode through the + lens of this component. + + If this component does not yield a dense log, return None (default behavior). + """ + return None + + +component_registry = Registry(BaseComponent) +"""The registry for Component classes. + +This creates a registry object for Component classes. This registry requires that all +added classes are subclasses of BaseComponent. To make a Component class available +through the registry, decorate the class definition with @component_registry.add. + +Example: + from ai_economist.foundation.base.base_component + import BaseComponent, component_registry + + @component_registry.add + class ExampleComponent(BaseComponent): + name = "Example" + pass + + assert component_registry.has("Example") + + ComponentClass = component_registry.get("Example") + component = ComponentClass(...) + assert isinstance(component, ExampleComponent) + +Notes: + The foundation package exposes the component registry as: foundation.components + + A Component class that is defined and registered following the above example will + only be visible in foundation.components if defined/registered in a file that is + imported in ../components/__init__.py. +""" diff --git a/ai_economist/foundation/base/base_env.py b/ai_economist/foundation/base/base_env.py new file mode 100644 index 0000000..051e6fa --- /dev/null +++ b/ai_economist/foundation/base/base_env.py @@ -0,0 +1,1157 @@ +# Copyright (c) 2020, salesforce.com, inc. +# All rights reserved. +# SPDX-License-Identifier: BSD-3-Clause +# For full license text, see the LICENSE file in the repo root +# or https://opensource.org/licenses/BSD-3-Clause + +import random +from abc import ABC, abstractmethod +from copy import deepcopy + +import numpy as np + +from ai_economist.foundation.agents import agent_registry +from ai_economist.foundation.base.registrar import Registry +from ai_economist.foundation.base.world import World +from ai_economist.foundation.components import component_registry +from ai_economist.foundation.entities import ( + endogenous_registry, + landmark_registry, + resource_registry, +) + + +class BaseEnvironment(ABC): + """ + Base Environment class. Should be used as the parent class for Scenario classes. + Instantiates world, agent, and component objects. + + Provides Gym-style API for resetting and stepping: + obs <-- env.reset() + obs, rew, done, info <-- env.step(actions) + + Also provides Gym-style API for controlling random behavior: + env.seed(seed) # Sets numpy and built-in RNG seeds to seed + + Reference: OpenAI Gym [https://github.com/openai/gym] + + Environments in this framework are instances of Scenario classes (which are built + as extensions of BaseEnvironment). A Scenario must implement the following + abstract methods (method docstrings provide detailed explanations): + reset_starting_layout + reset_agent_states + scenario_step + generate_observations + compute_reward + + Scenario classes define their own passive dynamics--that is, dynamics that do not + depend on agent actions--and supply observations. It is up to the Scenario class + to handle reward. + + Interactions with the environment are handled through components, which define + actions that agents can perform. Components are defined through distinct + Component classes (which extend BaseComponent [see base_component.py]) and must + be included in the components_registry in order to be used (see below). + Components influence the environment dynamics through effects they have on + agent/world states. They also (optionally) supply observations. + + The actions available to the agents, observations they receive, the dynamics of + the environment, and the rewards depend of the choice of which Scenario class and + Component class(es) to use. + + In multi_action_mode, an agent may choose an action for each of the action + subspaces defined by the included Component classes. A Component can define 0, 1, + or several action subspaces for a given agent type. If not using + multi_action_mode, these action subspaces are combined into a single action space + and the agent may select one action within this aggregated space. + + For additional detail regarding actions and action subspaces, see the + BaseComponent class in base_component.py. + + There are 2 types of agents: mobile agents and the planner agent. There can be + two or more mobile agents and a single planner agent. Conceptually, mobile agents + represent the individual actors in the economic simulation while the planner + agent represents a social planner that sets macroeconomic policy. + + This environment framework makes extensive use of Python classes. Scenarios, + Components, Agents, and environment entities such as Resources, Landmarks, + and Endogenous variables are all implemented as classes. These classes are + accessed via registries. See top example. + + Example: + from ai_economist import foundation + # foundation.scenarios <-- Scenario class registry + # foundation.components <-- Component class registry + # foundation.agents <-- Agent class registry + # foundation.resources <-- Resource class registry + # foundation.landmarks <-- Landmark class registry + # foundation.endogenous <-- Endogenous class registry + + # see ../scenarios/simple_wood_and_stone/dynamic_layout.py + UniScenarioClass = foundation.scenarios.get("uniform/simple_wood_and_stone") + + # see ../components/build.py and ../components/move.py + BuildComponentClass = foundation.components.get("Build") + GatherComponentClass = foundation.components.get("Gather") + + Example: + from ai_economist import foundation + from ai_economist.foundation.base.base_env import BaseEnvironment + + ScenarioClass = foundation.scenarios.get(...) + assert issubclass(ScenarioClass, BaseEnvironment) + + env = ScenarioClass( + components=[ + ("Build", {"payment": 20}), + ("Gather", {"move_labor": 1.0, "collect_labor": 2.0}), + ], + n_agents=20, + world_size=[25, 25], + ) + + obs = env.reset() + + actions = {agent.idx: ... for agent in env.all_agents} + obs, rew, done, info = env.step(actions) + + Args: + components (list): A list of tuples ("Component Name", {Component kwargs}) or + list of dicts {"Component Name": {Component kwargs}} specifying the + components that the instantiated environment will include. + "Component Name" must be a string matching the name of a registered + Component class. + {Component kwargs} must be a dictionary of kwargs that can be passed as + arguments to the Component class with name "Component Name". + Resetting, stepping, and observation generation will be carried out in + the order in which components are listed. This should be considered, + as re-ordering the components list may impact the dynamics of the + environment. + agent_composition (dict): Agent Class name in string paired with amount of agents of that class + n_agents (int): The number of mobile agents (does not include planner). + Number of agents must be > 1. + world_size (list): A length-2 list specifying the dimensions of the 2D world. + Interpreted as [height, width]. + episode_length (int): Number of timesteps in a single episode. + multi_action_mode_agents (bool): Whether mobile agents use multi_action_mode. + multi_action_mode_planner (bool): Whether the planner uses multi_action_mode. + flatten_observations (bool): Whether to preprocess observations by + concatenating all scalar/vector observation subfields into a single + "flat" observation field. If not, return observations as minimally + processed dictionaries. + flatten_masks (bool): Whether to flatten action masks into a single array or + to keep as a {"action_subspace_name": action_subspace_mask} dictionary. + For integration with deep RL, it is helpful to set this to True, for the + purpose of action masking: flattened masks have the same semantics as + policy logits. + allow_observation_scaling (bool): Whether to enable certain observation + fields to be scaled to a range better suited for deep RL. + dense_log_frequency (int): [optional] How often (in completed episodes) to + create a dense log while playing an episode. By default, dense logging is + turned off (dense_log_frequency=None). If dense_log_frequency=20, + a dense log will be created when the total episode count is a multiple of + 20. + Dense logs provide a log of agent states, actions, and rewards at each + timestep of an episode. They also log world states at a coarser timescale + (see below). Component classes optionally contribute additional + information to the dense log. + Note: dense logging is time consuming (especially with many agents). + world_dense_log_frequency (int): When dense logging, how often (in timesteps) to + log a snapshot of the world state. If world_dense_log_frequency=50 + (the default), the world state will be included in the dense log for + timesteps where t is a multiple of 50. + Note: More frequent world snapshots increase the dense log memory footprint. + seed (int, optional): If provided, sets the numpy and built-in random number + generator seeds to seed. You can control the seed after env construction + using the 'seed' method. + """ + + # The name associated with this Scenario class (must be unique) + # Note: This is what will identify the Scenario class in the scenario registry. + name = "" + + # The (sub)classes of agents that this scenario applies to + agent_subclasses = [] + + # The (non-agent) game entities that are expected to be in play + required_entities = None # Replace with list or tuple (can be empty) + + def __init__( + self, + components=None, + agent_composition=None, + world_size=None, + episode_length=1000, + multi_action_mode_agents=False, + multi_action_mode_planner=True, + flatten_observations=True, + flatten_masks=True, + allow_observation_scaling=True, + dense_log_frequency=None, + world_dense_log_frequency=50, + collate_agent_step_and_reset_data=False, + seed=None, + ): + + # Make sure a name was declared by child class + assert self.name + # Make sure the agent_subclasses was declared by child class + # and does not create potential conflicts + assert isinstance(self.agent_subclasses, (tuple, list)) + assert len(self.agent_subclasses) > 0 + if len(self.agent_subclasses) > 1: + for i in range(len(self.agent_subclasses)): + for j in range(len(self.agent_subclasses)): + if i == j: + continue + a_i = agent_registry.get(self.agent_subclasses[i]) + a_j = agent_registry.get(self.agent_subclasses[j]) + assert not issubclass(a_i, a_j) + + # Make sure the required_entities was declared by child class + # (will typecheck later) + assert isinstance(self.required_entities, (tuple, list)) + + # World size must be a tuple or list of length 2, + # specifying [Height, Width] of the game map + assert isinstance(world_size, (tuple, list)) + assert len(world_size) == 2 + self.world_size = world_size + + # Set n_agents + self.agent_composition=agent_composition + n_agents=0 + for k,v in agent_composition: + n_agents+=v + + # Number of agents must be an integer and there must be at least 2 agents + assert isinstance(n_agents, int) + assert n_agents >= 2 + self.n_agents = n_agents + + # Foundation assumes there's only a single planner + n_planners = 1 + self.num_agents = ( + n_agents + n_planners + ) # used in the warp_drive env wrapper (+ 1 for the planner) + + # Components must be a tuple/list where each element is either a... + # tuple: ('Component Name', {Component kwargs}) + # dict : {'Component Name': {Component kwargs}} + assert isinstance(components, (tuple, list)) + + def spec_is_valid(spec): + """Return True if component specification is validly configured.""" + if isinstance(spec, (tuple, list)): + if len(spec) != 2: + return False + return isinstance(spec[0], str) and isinstance(spec[1], dict) + if isinstance(spec, dict): + if len(spec) != 1: + return False + key_is_str = isinstance(list(spec.keys())[0], str) + val_is_dict = isinstance(list(spec.values())[0], dict) + return key_is_str and val_is_dict + return False + + assert all(spec_is_valid(component) for component in components) + + self._episode_length = int(episode_length) + assert self._episode_length >= 1 + + # Can an agent/planner execute multiple actions (1 per action subspace) per + # timestep (=True) or just one action (=False) + self.multi_action_mode_agents = bool(multi_action_mode_agents) + self.multi_action_mode_planner = bool(multi_action_mode_planner) + + # Whether to allow the world to scale observations + self._allow_observation_scaling = bool(allow_observation_scaling) + + # Whether to flatten the observation dictionaries before returning them + # Note: flattened observations are still returned as dictionaries, but with + # all scalar/vector observation fields concatenated into a single "flat" field. + self._flatten_observations = bool(flatten_observations) + + # Whether to flatten the mask dictionaries before putting them in the obs + self._flatten_masks = bool(flatten_masks) + + # How often (in episode completions) to create a dense log + self._dense_log_this_episode = False + if dense_log_frequency is None: # Only create a dense log + # if manually specified during reset + self._create_dense_log_every = None + else: # Create a dense log every dense_log_frequency episodes + self._create_dense_log_every = int(dense_log_frequency) + assert self._create_dense_log_every >= 1 + + # How often (in timesteps) to snapshot the world map when creating the denselog + self._world_dense_log_frequency = int(world_dense_log_frequency) + assert self._world_dense_log_frequency >= 1 + + # Seed control + if seed is not None: + self.seed(seed) + + # Initialize the set of entities used in the game that's being created. + # Coin and Labor are always included. + self._entities = { + "resources": ["Coin"], + "landmarks": [], + "endogenous": ["Labor"], + } + self._register_entities(self.required_entities) + + # Register all the components to get the entities they rely on. + self._components = [] + self._components_dict = {} + self._shorthand_lookup = {} + component_classes = [] + for component_spec in components: + if isinstance(component_spec, (tuple, list)): + component_name, component_config = component_spec + elif isinstance(component_spec, dict): + assert len(component_spec) == 1 + component_name = list(component_spec.keys())[0] + component_config = list(component_spec.values())[0] + else: + raise TypeError + component_cls = component_registry.get(component_name) + self._register_entities(component_cls.required_entities) + component_classes.append([component_cls, component_config]) + + # Initialize the world object (contains agents and world map), + # now that we know all the entities we'll use. + self.world = World( + self.world_size, + self.n_agents, + self.resources, + self.landmarks, + self.multi_action_mode_agents, + self.multi_action_mode_planner, + ) + + # Initialize the component objects. + for component_cls, component_kwargs in component_classes: + component_object = component_cls( + self.world, + self._episode_length, + inventory_scale=self.inv_scale, + **component_kwargs + ) + self._components.append(component_object) + self._components_dict[component_object.name] = component_object + self._shorthand_lookup[component_object.shorthand] = component_object + + # Register the components with the agents + # to finish setting up their state/action spaces. + for agent in self.world.agents: + agent.register_inventory(self.resources) + agent.register_endogenous(self.endogenous) + agent.register_components(self._components) + self.world.planner.register_inventory(self.resources) + self.world.planner.register_components(self._components) + + self._agent_lookup = {str(agent.idx): agent for agent in self.all_agents} + + self._completions = 0 + + self._last_ep_metrics = None + + # For dense logging + self._dense_log = {"world": [], "states": [], "actions": [], "rewards": []} + self._last_ep_dense_log = self.dense_log.copy() + + # For episode replay + self._replay_log = {"reset": dict(seed_state=None), "step": []} + self._last_ep_replay_log = self.replay_log.copy() + + self._packagers = {} + + # To collate all the agents ('0', '1', ...) data during reset and step + # into a single agent with index 'a' + self.collate_agent_step_and_reset_data = collate_agent_step_and_reset_data + + def _register_entities(self, entities): + for entity in entities: + if resource_registry.has(entity): + if entity not in self._entities["resources"]: + self._entities["resources"].append(entity) + elif landmark_registry.has(entity): + if entity not in self._entities["landmarks"]: + self._entities["landmarks"].append(entity) + elif endogenous_registry.has(entity): + if entity not in self._entities["endogenous"]: + self._entities["endogenous"].append(entity) + else: + raise KeyError("Unknown entity: {}".format(entity)) + + # Properties + # ---------- + + @property + def episode_length(self): + """Length of an episode, in timesteps.""" + return int(self._episode_length) + + @property + def inv_scale(self): + """Scale value to be used for inventory scaling. 1 if no scaling enabled.""" + return 0.01 if self._allow_observation_scaling else 1 + + @property + def resources(self): + """List of resources managed by this environment instance.""" + return sorted(list(self._entities["resources"])) + + @property + def landmarks(self): + """List of landmarks managed by this environment instance.""" + return sorted(list(self._entities["landmarks"])) + + @property + def endogenous(self): + """List of endogenous quantities managed by this environment instance.""" + return sorted(list(self._entities["endogenous"])) + + @property + def all_agents(self): + """List of mobile agents and the planner agent.""" + return self.world.agents + [self.world.planner] + + @property + def previous_episode_metrics(self): + """Metrics from the end of the last completed episode.""" + return self._last_ep_metrics + + @property + def metrics(self): + """The combined metrics yielded by the scenario and the components.""" + metrics = self.scenario_metrics() or {} + + for component in self._components: + m_metrics = component.get_metrics() + if not m_metrics: + continue + for k, v in m_metrics.items(): + metrics["{}/{}".format(component.shorthand, k)] = v + + return metrics + + @property + def components(self): + """The list of components associated with this scenario.""" + return self._components + + @property + def dense_log(self): + """The contents of the current (potentially incomplete) dense log.""" + return self._dense_log + + @property + def replay_log(self): + """The contents of the current (potentially incomplete) replay log.""" + return self._replay_log + + @property + def previous_episode_dense_log(self): + """Dense log from the last completed episode that was being logged.""" + return self._last_ep_dense_log + + @property + def previous_episode_replay_log(self): + """ + Replay log from the last completed episode. Serves as a compact encoding of + an episode by allowing the episode to be perfectly reproduced. + + Examples: + # replay log of the episode to be reproduced + replay_log = env.previous_episode_replay_log + + # recover episode metrics and dense log via replay + _ = env.reset(force_dense_logging=True, **replay_log['reset']) + for replay_step in replay_log['step']: + _ = env.step(**replay_step) + dense_log = env.previous_episode_dense_log + metrics = env.previous_episode_metrics + """ + return self._last_ep_replay_log + + @property + def generate_rewards(self): + """Compute the rewards for each agent.""" + return self._generate_rewards + + # Seed control + # ----------------- + + @staticmethod + def seed(seed): + """Sets the numpy and built-in random number generator seed. + + Args: + seed (int, float): Seed value to use. Must be > 0. Converted to int + internally if provided value is a float. + """ + assert isinstance(seed, (int, float)) + seed = int(seed) + assert seed > 0 + + np.random.seed(seed) + random.seed(seed) + + # Getters & Setters + # ----------------- + + def get_component(self, component_name): + """ + Get the component object instance wrapped in the environment. + + Args: + component_name (str): Name or shorthand name of the Component class to get. + Must correspond to a name or shorthand of one of the components that + is included in this environment instance. + + Returns: + component (BaseComponent object) + """ + if component_name not in self._components_dict: + if component_name not in self._shorthand_lookup: + raise KeyError( + "No component with name or shorthand name {} found; " + "registered components are:\n".format(component_name) + + "\n\t".join(list(self._components_dict.keys())) + ) + return self._shorthand_lookup[component_name] + return self._components_dict[component_name] + + def get_agent(self, agent_idx): + """ + Get the agent object instance with idx agent_idx. + + Args: + agent_idx (int or str): Identifier of the agent to return. Must match the + idx property of one of the agent objects in self.all_agents. + + Returns: + agent (BaseAgent object) + """ + agent = self._agent_lookup.get(str(agent_idx), None) + if agent is None: + raise ValueError("No agent with associated index {}".format(agent_idx)) + return agent + + def set_agent_component_action(self, agent_idx, component_name, action): + """ + Set agent with idx to take action for the action + subspace with name + + Args: + agent_idx (int or str): Identifier of the agent taking the action. Must + match the idx property of one of the agent objects in self.all_agents. + component_name (str): Name of the action subspace to set the action value + of. + action (int): Index of the chosen action. + """ + agent = self.get_agent(agent_idx) + agent.set_component_action(component_name, action) + + def parse_actions(self, action_dictionary): + """Put actions into the appropriate agent's action buffer""" + for agent_idx, agent_actions in action_dictionary.items(): + agent = self.get_agent(agent_idx) + agent.parse_actions(agent_actions) + + # Core control of environment execution + # ------------------------------------- + + @staticmethod + def _build_packager(sub_obs, put_in_both=None): + """ + Decides which keys-vals should be flattened or not. + put_in_both: include in both (e.g., 'time') + """ + if put_in_both is None: + put_in_both = [] + keep_as_is = [] + flatten = [] + wrap_as_list = {} + for k, v in sub_obs.items(): + if isinstance(v, np.ndarray): + multi_d_array = len(v.shape) > 1 + else: + multi_d_array = False + + if k == "action_mask" or multi_d_array: + keep_as_is.append(k) + else: + flatten.append(k) + if k in put_in_both: + keep_as_is.append(k) + + wrap_as_list[k] = np.isscalar(v) + + flatten = sorted(flatten) + + return keep_as_is, flatten, wrap_as_list + + @staticmethod + def _package(obs_dict, keep_as_is, flatten, wrap_as_list): + new_obs = {k: obs_dict[k] for k in keep_as_is} + if len(flatten) == 1: + k = flatten[0] + o = obs_dict[k] + if wrap_as_list[k]: + o = [o] + new_obs["flat"] = np.array(o, dtype=np.float32) + else: + to_flatten = [ + [obs_dict[k]] if wrap_as_list[k] else obs_dict[k] for k in flatten + ] + try: + new_obs["flat"] = np.concatenate(to_flatten).astype(np.float32) + except ValueError: + for k, v in zip(flatten, to_flatten): + print(k, np.array(v).shape) + print(v) + print("") + raise + return new_obs + + def _generate_observations(self, flatten_observations=False, flatten_masks=False): + def recursive_listify(d): + assert isinstance(d, dict) + for k, v in d.items(): + if isinstance(v, dict): + d[k] = recursive_listify(v) + elif isinstance(v, (int, float)): + d[k] = v + elif isinstance(v, (list, tuple, set)): + d[k] = list(v) + elif isinstance(v, (np.ndarray, np.integer, np.floating)): + d[k] = v.tolist() + else: + raise NotImplementedError( + "Not clear how to handle {} with type {}".format(k, type(v)) + ) + if isinstance(d[k], list) and len(d[k]) == 1: + d[k] = d[k][0] + return d + + # Initialize empty observations + if self.collate_agent_step_and_reset_data: + obs = {"a": {}, "p": {}} + else: + obs = {str(agent.idx): {} for agent in self.all_agents} + agent_wise_planner_obs = { + "p" + str(agent.idx): {} for agent in self.world.agents + } + + # Get/process observations generated by the scenario + world_obs = {str(k): v for k, v in self.generate_observations().items()} + time_scale = self.episode_length if self._allow_observation_scaling else 1.0 + for idx, o in world_obs.items(): + if idx in obs: + obs[idx].update({"world-" + k: v for k, v in o.items()}) + if self.collate_agent_step_and_reset_data and idx == "a": + obs[idx]["time"] = np.array( + [ + self.world.timestep / time_scale + for _ in range(self.world.n_agents) + ] + ) + else: + obs[idx]["time"] = [self.world.timestep / time_scale] + elif idx in agent_wise_planner_obs: + agent_wise_planner_obs[idx].update( + {"world-" + k: v for k, v in o.items()} + ) + else: + raise KeyError + + # Get/process observations generated by the components + for component in self._components: + for idx, o in component.obs().items(): + if idx in obs: + obs[idx].update({component.name + "-" + k: v for k, v in o.items()}) + elif idx in agent_wise_planner_obs: + agent_wise_planner_obs[idx].update( + {component.name + "-" + k: v for k, v in o.items()} + ) + else: + raise KeyError + + # Process the observations + if flatten_observations: + for o_dict in [obs, agent_wise_planner_obs]: + for aidx, aobs in o_dict.items(): + if not aobs: + continue + if aidx not in self._packagers: + self._packagers[aidx] = self._build_packager( + aobs, put_in_both=["time"] + ) + try: + o_dict[aidx] = self._package(aobs, *self._packagers[aidx]) + except ValueError: + print("Error when packaging obs.") + print("Agent index: {}\nRaw obs: {}\n".format(aidx, aobs)) + raise + + for k, v in agent_wise_planner_obs.items(): + if len(v) > 0: + obs[self.world.planner.idx][k] = ( + v["flat"] if flatten_observations else v + ) + + # Get each agent's action masks and incorporate them into the observations + for aidx, amask in self._generate_masks(flatten_masks=flatten_masks).items(): + obs[aidx]["action_mask"] = amask + + return obs + + def _generate_masks(self, flatten_masks=True): + if self.collate_agent_step_and_reset_data: + masks = {"a": {}, "p": {}} + else: + masks = {agent.idx: {} for agent in self.all_agents} + for component in self._components: + # Use the component's generate_masks method to get action masks + component_masks = component.generate_masks(completions=self._completions) + + for idx, mask in component_masks.items(): + if isinstance(mask, dict): + for sub_action, sub_mask in mask.items(): + masks[idx][ + "{}.{}".format(component.name, sub_action) + ] = sub_mask + else: + masks[idx][component.name] = mask + + if flatten_masks: + if self.collate_agent_step_and_reset_data: + flattened_masks = {} + for agent_id in masks.keys(): + if agent_id == "a": + multi_action_mode = self.multi_action_mode_agents + no_op_mask = np.ones((1, self.n_agents)) + elif agent_id == "p": + multi_action_mode = self.multi_action_mode_planner + no_op_mask = [1] + mask_dict = masks[agent_id] + list_of_masks = [] + if not multi_action_mode: + list_of_masks.append(no_op_mask) + for m in mask_dict.keys(): + if multi_action_mode: + list_of_masks.append(no_op_mask) + list_of_masks.append(mask_dict[m]) + flattened_masks[agent_id] = np.concatenate( + list_of_masks, axis=0 + ).astype(np.float32) + return flattened_masks + return { + str(agent.idx): agent.flatten_masks(masks[agent.idx]) + for agent in self.all_agents + } + return { + str(agent_idx): { + k: np.array(v, dtype=np.uint8).tolist() + for k, v in masks[agent_idx].items() + } + for agent_idx in list(masks.keys()) + } + + def _generate_rewards(self): + rew = self.compute_reward() + assert isinstance(rew, dict) + return {str(k): v for k, v in rew.items()} + + def _finalize_logs(self): + self._last_ep_replay_log = self._replay_log + self._last_ep_metrics = self.metrics + + if not self._dense_log_this_episode: + return + + def recursive_cast(d): + if isinstance(d, (list, tuple, set)): + new_d = [recursive_cast(v_) for v_ in d] + return new_d + if isinstance(d, dict): + for k, v in d.items(): + if isinstance(v, (list, tuple, set, dict)): + d[k] = recursive_cast(v) + elif isinstance(v, (int, float, str)): + d[k] = v + elif isinstance(v, (np.ndarray, np.integer, np.floating)): + d[k] = v.tolist() + else: + raise NotImplementedError( + "Not clear how to handle {} with type {}".format(k, type(v)) + ) + return d + if isinstance(d, (int, float, str)): + return d + if isinstance(d, (np.ndarray, np.integer, np.floating)): + return d.tolist() + raise NotImplementedError( + "Not clear how to handle {} with type {}".format(d, type(d)) + ) + + self._dense_log["world"].append(deepcopy(self.world.maps.state_dict)) + self._dense_log["states"].append( + {str(agent.idx): deepcopy(agent.state) for agent in self.all_agents} + ) + + # Back-fill the log with each component's dense log to complete the aggregate + # dense log + for component in self._components: + component_log = component.get_dense_log() + if component_log is None: + continue + if isinstance(component_log, dict): + for k, v in component_log.items(): + self._dense_log[component.shorthand + "-" + k] = v + elif isinstance(component_log, (tuple, list)): + self._dense_log[component.shorthand] = list(component_log) + else: + raise TypeError + + self._last_ep_dense_log = recursive_cast(self._dense_log) + + def collate_agent_obs(self, obs): + # Collating observations from all agents + if "a" in obs: # already collated! + return obs + num_agents = len(obs.keys()) - 1 + obs["a"] = {} + for key in obs["0"].keys(): + obs["a"][key] = np.stack( + [obs[str(agent_idx)][key] for agent_idx in range(num_agents)], axis=-1 + ) + for agent_idx in range(num_agents): + del obs[str(agent_idx)] + return obs + + def collate_agent_rew(self, rew): + # Collating rewards from all agents + if "a" in rew: # already collated! + return rew + num_agents = len(rew.keys()) - 1 + rew["a"] = [] + for agent_idx in range(num_agents): + rew["a"] += [rew[str(agent_idx)]] + del rew[str(agent_idx)] + return rew + + def collate_agent_info(self, info): + # Collating infos from all agents + if "a" in info: # already collated! + return info + num_agents = len(info.keys()) - 1 + info["a"] = {} + for agent_idx in range(num_agents): + info["a"][str(agent_idx)] = info[str(agent_idx)] + del info[str(agent_idx)] + return info + + def reset(self, seed_state=None, force_dense_logging=False): + """ + Reset the state of the environment to initialize a new episode. + + Arguments: + seed_state (tuple or list): Optional state that the numpy RNG should be set + to prior to the reset cycle must be length 5, following the format + expected by np.random.set_state() + force_dense_logging (bool): Optional whether to force dense logging to take + place this episode; default behavior is to do dense logging every + create_dense_log_every episodes + + Returns: + obs (dict): A dictionary of {"agent_idx": agent_obs} with an entry for + each agent receiving observations. The "agent_idx" key identifies the + agent receiving the observations in the associated agent_obs value, + which itself is a dictionary. The "agent_idx" key matches the + agent.idx property for the given agent. + """ + if seed_state is not None: + assert isinstance(seed_state, (tuple, list)) + assert len(seed_state) == 5 + seed_state = ( + str(seed_state[0]), + np.array(seed_state[1], dtype=np.uint32), + int(seed_state[2]), + int(seed_state[3]), + float(seed_state[4]), + ) + np.random.set_state(seed_state) + + if force_dense_logging: + self._dense_log_this_episode = True + elif self._create_dense_log_every is None: + self._dense_log_this_episode = False + else: + self._dense_log_this_episode = ( + self._completions % self._create_dense_log_every + ) == 0 + + # For dense logging + self._dense_log = {"world": [], "states": [], "actions": [], "rewards": []} + + # For episode replay + self._replay_log = {"reset": dict(seed_state=np.random.get_state()), "step": []} + + # Reset the timestep counter + self.world.timestep = 0 + + # Perform the scenario reset, + # which includes resetting the world and agent states + self.reset_starting_layout() + self.reset_agent_states() + + # Perform the component resets for each registered component + for component in self._components: + component.reset() + + # Take any customized reset actions + self.additional_reset_steps() + + # By default, agents take the NO-OP action for each action space. + # Reset actions to that default. + for agent in self.all_agents: + agent.reset_actions() + + # Produce observations + obs = self._generate_observations( + flatten_observations=self._flatten_observations, + flatten_masks=self._flatten_masks, + ) + + if self.collate_agent_step_and_reset_data: + obs = self.collate_agent_obs(obs) + + return obs + + def step(self, actions=None, seed_state=None): + """ + Execute the components, perform the scenario step, collect observations and + return observations, rewards, dones, and infos. + + Arguments: + actions (dict): dictionary of {agent_idx: action} with an entry for each + agent (which may include the planner) that is supplying an action. + The key identifies which agent the action is associated with. It + should match that agent's agent.idx property. + The value indicates which action the agent will take. The environment + supports two formats for specifying an action, with slightly + different expectations for multi_action_mode. + If agent.multi_action_mode, action must be a list of integers + specifying the chosen action for each action subspace. + Otherwise, action must be a single integer specifying the chosen + action (where the action space is the concatenation of the subspaces). + seed_state (tuple or list): Optional state that the numpy RNG should be set + to prior to the reset cycle must be length 5, following the format + expected by np.random.set_state(). + + Returns: + obs (dict): A dictionary of {"agent_idx": agent_obs} with an entry for + each agent receiving observations. The "agent_idx" key identifies the + agent receiving the observations in the associated agent_obs value, + which itself is a dictionary. The "agent_idx" key matches the + agent.idx property for the given agent. + rew (dict): A dictionary of {"agent_idx": reward} with an entry for each + agent that also receives an observation. Each reward value is a scalar. + done (dict): A dictionary with a single key "__all__". The associated + value is False when self.world.timestep < self.episode_length and True + otherwise. + info (dict): Placeholder dictionary with structure {"agent_idx": {}}, + with the same keys as obs and rew. + """ + if actions is not None: + assert isinstance(actions, dict) + self.parse_actions(actions) + + if seed_state is not None: + assert isinstance(seed_state, (tuple, list)) + assert len(seed_state) == 5 + seed_state = ( + str(seed_state[0]), + np.array(seed_state[1], dtype=np.uint32), + int(seed_state[2]), + int(seed_state[3]), + float(seed_state[4]), + ) + np.random.set_state(seed_state) + + self._replay_log["step"].append( + dict(actions=actions, seed_state=np.random.get_state()) + ) + + if self._dense_log_this_episode: + self._dense_log["world"].append( + deepcopy(self.world.maps.state_dict) + if (self.world.timestep % self._world_dense_log_frequency) == 0 + else {} + ) + self._dense_log["states"].append( + {str(agent.idx): deepcopy(agent.state) for agent in self.all_agents} + ) + self._dense_log["actions"].append( + { + str(agent.idx): {k: v for k, v in agent.action.items() if v > 0} + for agent in self.all_agents + } + ) + + self.world.timestep += 1 + + for component in self._components: + component.component_step() + + self.scenario_step() + + obs = self._generate_observations( + flatten_observations=self._flatten_observations, + flatten_masks=self._flatten_masks, + ) + rew = self._generate_rewards() + done = {"__all__": self.world.timestep >= self._episode_length} + info = {k: {} for k in obs.keys()} + + if self._dense_log_this_episode: + self._dense_log["rewards"].append(rew) + + for agent in self.all_agents: + agent.reset_actions() + + if done[ + "__all__" + ]: # Complete the dense log and stash it as well as the metrics + self._finalize_logs() + self._completions += 1 + + if self.collate_agent_step_and_reset_data: + obs = self.collate_agent_obs(obs) + rew = self.collate_agent_rew(rew) + info = self.collate_agent_info(info) + + return obs, rew, done, info + + # The following methods must be implemented for each scenario + # ----------------------------------------------------------- + + @abstractmethod + def reset_starting_layout(self): + """ + Part 1/2 of scenario reset. This method handles resetting the state of the + environment managed by the scenario (i.e. resource & landmark layout). + """ + + @abstractmethod + def reset_agent_states(self): + """ + Part 2/2 of scenario reset. This method handles resetting the state of the + agents themselves (i.e. inventory, locations, etc.). + """ + + @abstractmethod + def scenario_step(self): + """ + Update the state of the world according to whatever rules this scenario + implements. + + This gets called in the 'step' method (of base_env) after going through each + component step and before generating observations, rewards, etc. + + This is where things like resource regeneration, income redistribution, etc., + can be implemented. + """ + + @abstractmethod + def generate_observations(self): + """ + Generate observations associated with this scenario. + + A scenario does not need to produce observations and can provide observations + for only some agent types; however, for a given agent type, it should either + always or never yield an observation. If it does yield an observation, + that observation should always have the same structure/sizes! + + Returns: + obs (dict): A dictionary of {agent.idx: agent_obs_dict}. In words, + return a dictionary with an entry for each agent (which can including + the planner) for which this scenario provides an observation. For each + entry, the key specifies the index of the agent and the value contains + its associated observation dictionary. + """ + + @abstractmethod + def compute_reward(self): + """ + Apply the reward function(s) associated with this scenario to get the rewards + from this step. + + Returns: + rew (dict): A dictionary of {agent.idx: agent_obs_dict}. In words, + return a dictionary with an entry for each agent in the environment + (including the planner). For each entry, the key specifies the index of + the agent and the value contains the scalar reward earned this timestep. + """ + + # Optional methods for customization + # ---------------------------------- + + def additional_reset_steps(self): + """ + Extra scenario-specific steps that should be performed at the end of the reset + cycle. + + For each reset cycle... + First, reset_starting_layout() and reset_agent_states() will be called. + + Second, .reset() will be called for each registered component. + + Lastly, this method will be called to allow for any final customization of + the reset cycle. + """ + + def scenario_metrics(self): + """ + Allows the scenario to generate metrics (collected along with component metrics + in the 'metrics' property). + + To have the scenario add metrics, this function needs to return a dictionary of + {metric_key: value} where 'value' is a scalar (no nesting or lists!) + """ + return + + +scenario_registry = Registry(BaseEnvironment) +"""The registry for Scenario classes. + +This creates a registry object for Scenario classes. This registry requires that all +added classes are subclasses of BaseEnvironment. To make a Scenario class available +through the registry, decorate the class definition with @scenario_registry.add. + +Example: + from ai_economist.foundation.base.base_env + import BaseEnvironment, scenario_registry + + @scenario_registry.add + class ExampleScenario(BaseEnvironment): + name = "Example" + pass + + assert scenario_registry.has("Example") + + ScenarioClass = scenario_registry.get("Example") + scenario = ScenarioClass(...) + assert isinstance(scenario, ExampleScenario) + +Notes: + The foundation package exposes the scenario registry as: foundation.scenarios + + A Scenario class that is defined and registered following the above example will + only be visible in foundation.scenarios if defined/registered in a file that is + imported in ../scenarios/__init__.py. +""" diff --git a/ai_economist/foundation/base/registrar.py b/ai_economist/foundation/base/registrar.py new file mode 100644 index 0000000..9635f57 --- /dev/null +++ b/ai_economist/foundation/base/registrar.py @@ -0,0 +1,103 @@ +# Copyright (c) 2020, salesforce.com, inc. +# All rights reserved. +# SPDX-License-Identifier: BSD-3-Clause +# For full license text, see the LICENSE file in the repo root +# or https://opensource.org/licenses/BSD-3-Clause + + +class Registry: + """Utility for registering sets of similar classes and looking them up by name. + + Registries provide a simple API for getting classes used to build environment + instances. Their main purpose is to organize such "building block" classes (i.e. + Components, Scenarios, Agents) for easy reference as well as to ensure that all + classes within a particular registry inherit from the same Base Class. + + Args: + base_class (class): The class that all entries in the registry must be a + subclass of. + + Example: + class BaseClass: + pass + + registry = Registry(BaseClass) + + @registry.add + class ExampleSubclassA(BaseClass): + name = "ExampleA" + pass + + @registry.add + class ExampleSubclassB(BaseClass): + name = "ExampleB" + pass + + print(registry.entries) + # ["ExampleA", "ExampleB"] + + assert registry.has("ExampleA") + assert registry.get("ExampleB") is ExampleSubclassB + """ + + def __init__(self, base_class=None): + self.base_class = base_class + self._entries = [] + self._lookup = dict() + + def add(self, cls): + """Add cls to this registry. + + Args: + cls: The class to add to this registry. Must be a subclass of + self.base_class. + + Returns: + cls (to allow decoration with @registry.add) + + See Registry class docstring for example. + """ + assert "." not in cls.name + if self.base_class: + assert issubclass(cls, self.base_class) + self._lookup[cls.name.lower()] = cls + if cls.name not in self._entries: + self._entries.append(cls.name) + return cls + + def get(self, cls_name): + """Return registered class with name cls_name. + + Args: + cls_name (str): Name of the registered class to get. + + Returns: + Registered class cls, where cls.name matches cls_name (ignoring casing). + + See Registry class docstring for example. + """ + if cls_name.lower() not in self._lookup: + raise KeyError('"{}" is not a name of a registered class'.format(cls_name)) + return self._lookup[cls_name.lower()] + + def has(self, cls_name): + """Return True if a class with name cls_name is registered. + + Args: + cls_name (str): Name of class to check. + + See Registry class docstring for example. + """ + return cls_name.lower() in self._lookup + + @property + def entries(self): + """Names of classes in this registry. + + Returns: + A list of strings corresponding to the names of classes registered in + this registry object. + + See Registry class docstring for example. + """ + return sorted(list(self._entries)) diff --git a/ai_economist/foundation/base/world.py b/ai_economist/foundation/base/world.py new file mode 100644 index 0000000..f453744 --- /dev/null +++ b/ai_economist/foundation/base/world.py @@ -0,0 +1,495 @@ +# Copyright (c) 2020, salesforce.com, inc. +# All rights reserved. +# SPDX-License-Identifier: BSD-3-Clause +# For full license text, see the LICENSE file in the repo root +# or https://opensource.org/licenses/BSD-3-Clause + +import numpy as np + +from ai_economist.foundation.agents import agent_registry +from ai_economist.foundation.entities import landmark_registry, resource_registry + + +class Maps: + """Manages the spatial configuration of the world as a set of entity maps. + + A maps object is built during world construction, which is a part of environment + construction. The maps object is accessible through the world object. The maps + object maintains a map state for each of the spatial entities that are involved + in the constructed environment (which are determined by the "required_entities" + attributes of the Scenario and Component classes used to build the environment). + + The Maps class also implements some of the basic spatial logic of the game, + such as which locations agents can occupy based on other agent locations and + locations of various landmarks. + + Args: + size (list): A length-2 list specifying the dimensions of the 2D world. + Interpreted as [height, width]. + n_agents (int): The number of mobile agents (does not include planner). + world_resources (list): The resources registered during environment + construction. + world_landmarks (list): The landmarks registered during environment + construction. + """ + + def __init__(self, size, n_agents, world_resources, world_landmarks): + self.size = size + self.sz_h, self.sz_w = size + + self.n_agents = n_agents + + self.resources = world_resources + self.landmarks = world_landmarks + self.entities = world_resources + world_landmarks + + self._maps = {} # All maps + self._blocked = [] # Solid objects that no agent can move through + self._private = [] # Solid objects that only permit movement for parent agents + self._public = [] # Non-solid objects that agents can move on top of + self._resources = [] # Non-solid objects that can be collected + + self._private_landmark_types = [] + self._resource_source_blocks = [] + + self._map_keys = [] + + self._accessibility_lookup = {} + + for resource in self.resources: + resource_cls = resource_registry.get(resource) + if resource_cls.collectible: + self._maps[resource] = np.zeros(shape=self.size) + self._resources.append(resource) + self._map_keys.append(resource) + + self.landmarks.append("{}SourceBlock".format(resource)) + + for landmark in self.landmarks: + dummy_landmark = landmark_registry.get(landmark)() + + if dummy_landmark.public: + self._maps[landmark] = np.zeros(shape=self.size) + self._public.append(landmark) + self._map_keys.append(landmark) + + elif dummy_landmark.blocking: + self._maps[landmark] = np.zeros(shape=self.size) + self._blocked.append(landmark) + self._map_keys.append(landmark) + self._accessibility_lookup[landmark] = len(self._accessibility_lookup) + + elif dummy_landmark.private: + self._private_landmark_types.append(landmark) + self._maps[landmark] = dict( + owner=-np.ones(shape=self.size, dtype=np.int16), + health=np.zeros(shape=self.size), + ) + self._private.append(landmark) + self._map_keys.append(landmark) + self._accessibility_lookup[landmark] = len(self._accessibility_lookup) + + else: + raise NotImplementedError + + self._idx_map = np.stack( + [i * np.ones(shape=self.size) for i in range(self.n_agents)] + ) + self._idx_array = np.arange(self.n_agents) + if self._accessibility_lookup: + self._accessibility = np.ones( + shape=[len(self._accessibility_lookup), self.n_agents] + self.size, + dtype=bool, + ) + self._net_accessibility = None + else: + self._accessibility = None + self._net_accessibility = np.ones( + shape=[self.n_agents] + self.size, dtype=bool + ) + + self._agent_locs = [None for _ in range(self.n_agents)] + self._unoccupied = np.ones(self.size, dtype=bool) + + def clear(self, entity_name=None): + """Clear resource and landmark maps.""" + if entity_name is not None: + assert entity_name in self._maps + if entity_name in self._private_landmark_types: + self._maps[entity_name] = dict( + owner=-np.ones(shape=self.size, dtype=np.int16), + health=np.zeros(shape=self.size), + ) + else: + self._maps[entity_name] *= 0 + + else: + for name in self.keys(): + self.clear(entity_name=name) + + if self._accessibility is not None: + self._accessibility = np.ones_like(self._accessibility) + self._net_accessibility = None + + def clear_agent_loc(self, agent=None): + """Remove agents or agent from the world map.""" + # Clear all agent locations + if agent is None: + self._agent_locs = [None for _ in range(self.n_agents)] + self._unoccupied[:, :] = 1 + + # Clear the location of the provided agent + else: + i = agent.idx + if self._agent_locs[i] is None: + return + r, c = self._agent_locs[i] + self._unoccupied[r, c] = 1 + self._agent_locs[i] = None + + def set_agent_loc(self, agent, r, c): + """Set the location of agent to [r, c]. + + Note: + Things might break if you set the agent's location to somewhere it + cannot access. Don't do that. + """ + assert (0 <= r < self.size[0]) and (0 <= c < self.size[1]) + i = agent.idx + # If the agent is currently on the board... + if self._agent_locs[i] is not None: + curr_r, curr_c = self._agent_locs[i] + # If the agent isn't actually moving, just return + if (curr_r, curr_c) == (r, c): + return + # Make the location the agent is currently at as unoccupied + # (since the agent is going to move) + self._unoccupied[curr_r, curr_c] = 1 + + # Set the agent location to the specified coordinates + # and update the occupation map + agent.state["loc"] = [r, c] + self._agent_locs[i] = [r, c] + self._unoccupied[r, c] = 0 + + def keys(self): + """Return an iterable over map keys.""" + return self._maps.keys() + + def values(self): + """Return an iterable over map values.""" + return self._maps.values() + + def items(self): + """Return an iterable over map (key, value) pairs.""" + return self._maps.items() + + def get(self, entity_name, owner=False): + """Return the map or ownership for entity_name.""" + assert entity_name in self._maps + if entity_name in self._private_landmark_types: + sub_key = "owner" if owner else "health" + return self._maps[entity_name][sub_key] + return self._maps[entity_name] + + def set(self, entity_name, map_state): + """Set the map for entity_name.""" + if entity_name in self._private_landmark_types: + assert "owner" in map_state + assert self.get(entity_name, owner=True).shape == map_state["owner"].shape + assert "health" in map_state + assert self.get(entity_name, owner=False).shape == map_state["health"].shape + + h = np.maximum(0.0, map_state["health"]) + o = map_state["owner"].astype(np.int16) + + o[h <= 0] = -1 + tmp = o[h > 0] + if len(tmp) > 0: + assert np.min(tmp) >= 0 + + self._maps[entity_name] = dict(owner=o, health=h) + + owned_by_agent = o[None] == self._idx_map + owned_by_none = o[None] == -1 + self._accessibility[ + self._accessibility_lookup[entity_name] + ] = np.logical_or(owned_by_agent, owned_by_none) + self._net_accessibility = None + + else: + assert self.get(entity_name).shape == map_state.shape + self._maps[entity_name] = np.maximum(0, map_state) + + if entity_name in self._blocked: + self._accessibility[ + self._accessibility_lookup[entity_name] + ] = np.repeat(map_state[None] == 0, self.n_agents, axis=0) + self._net_accessibility = None + + def set_add(self, entity_name, map_state): + """Add map_state to the existing map for entity_name.""" + assert entity_name not in self._private_landmark_types + self.set(entity_name, self.get(entity_name) + map_state) + + def get_point(self, entity_name, r, c, **kwargs): + """Return the entity state at the specified coordinates.""" + point_map = self.get(entity_name, **kwargs) + return point_map[r, c] + + def set_point(self, entity_name, r, c, val, owner=None): + """Set the entity state at the specified coordinates.""" + if entity_name in self._private_landmark_types: + assert owner is not None + h = self._maps[entity_name]["health"] + o = self._maps[entity_name]["owner"] + assert o[r, c] == -1 or o[r, c] == int(owner) + h[r, c] = np.maximum(0, val) + if h[r, c] == 0: + o[r, c] = -1 + else: + o[r, c] = int(owner) + + self._maps[entity_name]["owner"] = o + self._maps[entity_name]["health"] = h + + self._accessibility[ + self._accessibility_lookup[entity_name], :, r, c + ] = np.logical_or(o[r, c] == self._idx_array, o[r, c] == -1).astype(bool) + self._net_accessibility = None + + else: + self._maps[entity_name][r, c] = np.maximum(0, val) + + if entity_name in self._blocked: + self._accessibility[ + self._accessibility_lookup[entity_name] + ] = np.repeat(np.array([val]) == 0, self.n_agents, axis=0) + self._net_accessibility = None + + def set_point_add(self, entity_name, r, c, value, **kwargs): + """Add value to the existing entity state at the specified coordinates.""" + self.set_point( + entity_name, + r, + c, + value + self.get_point(entity_name, r, c, **kwargs), + **kwargs + ) + + def is_accessible(self, r, c, agent_id): + """Return True if agent with id agent_id can occupy the location [r, c].""" + return bool(self.accessibility[agent_id, r, c]) + + def location_resources(self, r, c): + """Return {resource: health} dictionary for any resources at location [r, c].""" + return { + k: self._maps[k][r, c] for k in self._resources if self._maps[k][r, c] > 0 + } + + def location_landmarks(self, r, c): + """Return {landmark: health} dictionary for any landmarks at location [r, c].""" + tmp = {k: self.get_point(k, r, c) for k in self.keys()} + return {k: v for k, v in tmp.items() if k not in self._resources and v > 0} + + @property + def unoccupied(self): + """Return a boolean map indicating which locations are unoccupied.""" + return self._unoccupied + + @property + def accessibility(self): + """Return a boolean map indicating which locations are accessible.""" + if self._net_accessibility is None: + self._net_accessibility = self._accessibility.prod(axis=0).astype(bool) + return self._net_accessibility + + @property + def empty(self): + """Return a boolean map indicating which locations are empty. + + Empty locations have no landmarks or resources.""" + return self.state.sum(axis=0) == 0 + + @property + def state(self): + """Return the concatenated maps of landmark and resources.""" + return np.stack([self.get(k) for k in self.keys()]).astype(np.float32) + + @property + def owner_state(self): + """Return the concatenated ownership maps of private landmarks.""" + return np.stack( + [self.get(k, owner=True) for k in self._private_landmark_types] + ).astype(np.int16) + + @property + def state_dict(self): + """Return a dictionary of the map states.""" + return self._maps + + +class World: + """Manages the environment's spatial- and agent-states. + + The world object represents the state of the environment, minus whatever state + information is implicitly maintained by separate components. The world object + maintains the spatial state through an instance of the Maps class. Agent states + are maintained through instances of Agent classes (subclasses of BaseAgent), + with one such instance for each of the agents in the environment. + + The world object is built during the environment construction, after the + required entities have been registered. As part of the world object construction, + it instantiates a map object and the agent objects. + + The World class adds some functionality for interfacing with the spatial state + (the maps object) and setting/resetting agent locations. But its function is + mostly to wrap the stateful, non-component environment objects. + + Args: + world_size (list): A length-2 list specifying the dimensions of the 2D world. + Interpreted as [height, width]. + n_agents (int): The number of total agents (does not include planner). + agent_composition(dict): Dict of Agent Class names and amount + world_resources (list): The resources registered during environment + construction. + world_landmarks (list): The landmarks registered during environment + construction. + multi_action_mode_agents (bool): Whether "mobile" agents use multi action mode + (see BaseEnvironment in base_env.py). + multi_action_mode_planner (bool): Whether the planner agent uses multi action + mode (see BaseEnvironment in base_env.py). + """ + + def __init__( + self, + world_size, + agent_composition, + world_resources, + world_landmarks, + multi_action_mode_agents, + multi_action_mode_planner, + ): + self.world_size = world_size + + self.resources = world_resources + self.landmarks = world_landmarks + self.multi_action_mode_agents = bool(multi_action_mode_agents) + self.multi_action_mode_planner = bool(multi_action_mode_planner) + self._agent_class_idx_map={} + #create agents + self.agent_composition=agent_composition + self.n_agents=0 + self._agents = [] + for k,v in agent_composition: + self._agent_class_idx_map[k]=[] + for offset in range(v): + agent_class=agent_registry.get(k) + self._agents.append(agent_class(self.n_agents,multi_action_mode_agents=self.multi_action_mode_agents)) + self._agent_class_idx_map[k].append(self.n_agents) + self.n_agents+=1 + self.maps = Maps(world_size, self.n_agents, world_resources, world_landmarks) + + planner_class = agent_registry.get("BasicPlanner") + self._planner = planner_class(multi_action_mode=self.multi_action_mode_planner) + + self.timestep = 0 + + # CUDA-related attributes (for GPU simulations). + # These will be set via the env_wrapper, if required. + self.use_cuda = False + self.cuda_function_manager = None + self.cuda_data_manager = None + + @property + def agents(self): + """Return a list of the agent objects in the world (sorted by index).""" + return self._agents + + @property + def planner(self): + """Return the planner agent object.""" + return self._planner + + @property + def loc_map(self): + """Return a map indicating the agent index occupying each location. + + Locations with a value of -1 are not occupied by an agent. + """ + idx_map = -np.ones(shape=self.world_size, dtype=np.int16) + for agent in self.agents: + r, c = agent.loc + idx_map[r, c] = int(agent.idx) + return idx_map + + def get_random_order_agents(self): + """The agent list in a randomized order.""" + agent_order = np.random.permutation(self.n_agents) + agents = self.agents + return [agents[i] for i in agent_order] + + def get_agent_class(self,idx): + """Return class name of agent""" + return self.agents[idx].name + + def is_valid(self, r, c): + """Return True if the coordinates [r, c] are within the game boundaries.""" + return (0 <= r < self.world_size[0]) and (0 <= c < self.world_size[1]) + + def is_location_accessible(self, r, c, agent): + """Return True if location [r, c] is accessible to agent.""" + if not self.is_valid(r, c): + return False + return self.maps.is_accessible(r, c, agent.idx) + + def can_agent_occupy(self, r, c, agent): + """Return True if location [r, c] is accessible to agent and unoccupied.""" + if not self.is_location_accessible(r, c, agent): + return False + if self.maps.unoccupied[r, c]: + return True + return False + + def clear_agent_locs(self): + """Take all agents off the board. Useful for resetting.""" + for agent in self.agents: + agent.state["loc"] = [-1, -1] + self.maps.clear_agent_loc() + + def agent_locs_are_valid(self): + """Returns True if all agent locations comply with world semantics.""" + return all( + self.is_location_accessible(*agent.loc, agent) for agent in self.agents + ) + + def set_agent_loc(self, agent, r, c): + """Set the agent's location to coordinates [r, c] if possible. + + If agent cannot occupy [r, c], do nothing.""" + if self.can_agent_occupy(r, c, agent): + self.maps.set_agent_loc(agent, r, c) + return [int(coord) for coord in agent.loc] + + def location_resources(self, r, c): + """Return {resource: health} dictionary for any resources at location [r, c].""" + if not self.is_valid(r, c): + return {} + return self.maps.location_resources(r, c) + + def location_landmarks(self, r, c): + """Return {landmark: health} dictionary for any landmarks at location [r, c].""" + if not self.is_valid(r, c): + return {} + return self.maps.location_landmarks(r, c) + + def create_landmark(self, landmark_name, r, c, agent_idx=None): + """Place a landmark on the world map. + + Place landmark of type landmark_name at the given coordinates, indicating + agent ownership if applicable.""" + self.maps.set_point(landmark_name, r, c, 1, owner=agent_idx) + + def consume_resource(self, resource_name, r, c): + """Consume a unit of resource_name from location [r, c].""" + self.maps.set_point_add(resource_name, r, c, -1) diff --git a/ai_economist/foundation/components/__init__.py b/ai_economist/foundation/components/__init__.py new file mode 100644 index 0000000..eb00ee6 --- /dev/null +++ b/ai_economist/foundation/components/__init__.py @@ -0,0 +1,19 @@ +# Copyright (c) 2020, salesforce.com, inc. +# All rights reserved. +# SPDX-License-Identifier: BSD-3-Clause +# For full license text, see the LICENSE file in the repo root +# or https://opensource.org/licenses/BSD-3-Clause + +from ai_economist.foundation.base.base_component import component_registry + +from . import ( + build, + continuous_double_auction, + covid19_components, + move, + redistribution, + simple_labor, +) + +# Import files that add Component class(es) to component_registry +# --------------------------------------------------------------- diff --git a/ai_economist/foundation/components/build.py b/ai_economist/foundation/components/build.py new file mode 100644 index 0000000..6bd2a1f --- /dev/null +++ b/ai_economist/foundation/components/build.py @@ -0,0 +1,266 @@ +# Copyright (c) 2020, salesforce.com, inc. +# All rights reserved. +# SPDX-License-Identifier: BSD-3-Clause +# For full license text, see the LICENSE file in the repo root +# or https://opensource.org/licenses/BSD-3-Clause + +import numpy as np + +from ai_economist.foundation.base.base_component import ( + BaseComponent, + component_registry, +) + + +@component_registry.add +class Build(BaseComponent): + """ + Allows mobile agents to build house landmarks in the world using stone and wood, + earning income. + + Can be configured to include heterogeneous building skill where agents earn + different levels of income when building. + + Args: + payment (int): Default amount of coin agents earn from building. + Must be >= 0. Default is 10. + payment_max_skill_multiplier (int): Maximum skill multiplier that an agent + can sample. Must be >= 1. Default is 1. + skill_dist (str): Distribution type for sampling skills. Default ("none") + gives all agents identical skill equal to a multiplier of 1. "pareto" and + "lognormal" sample skills from the associated distributions. + build_labor (float): Labor cost associated with building a house. + Must be >= 0. Default is 10. + """ + + name = "Build" + component_type = "Build" + required_entities = ["Wood", "Stone", "Coin", "House", "Labor"] + agent_subclasses = ["BasicMobileAgent"] + + def __init__( + self, + *base_component_args, + payment=10, + payment_max_skill_multiplier=1, + skill_dist="none", + build_labor=10.0, + **base_component_kwargs + ): + super().__init__(*base_component_args, **base_component_kwargs) + + self.payment = int(payment) + assert self.payment >= 0 + + self.payment_max_skill_multiplier = int(payment_max_skill_multiplier) + assert self.payment_max_skill_multiplier >= 1 + + self.resource_cost = {"Wood": 1, "Stone": 1} + + self.build_labor = float(build_labor) + assert self.build_labor >= 0 + + self.skill_dist = skill_dist.lower() + assert self.skill_dist in ["none", "pareto", "lognormal"] + + self.sampled_skills = {} + + self.builds = [] + + def agent_can_build(self, agent): + """Return True if agent can actually build in its current location.""" + # See if the agent has the resources necessary to complete the action + for resource, cost in self.resource_cost.items(): + if agent.state["inventory"][resource] < cost: + return False + + # Do nothing if this spot is already occupied by a landmark or resource + if self.world.location_resources(*agent.loc): + return False + if self.world.location_landmarks(*agent.loc): + return False + # If we made it here, the agent can build. + return True + + # Required methods for implementing components + # -------------------------------------------- + + def get_n_actions(self, agent_cls_name): + """ + See base_component.py for detailed description. + + Add a single action (build) for mobile agents. + """ + # This component adds 1 action that mobile agents can take: build a house + if agent_cls_name == "BasicMobileAgent": + return 1 + + return None + + def get_additional_state_fields(self, agent_cls_name): + """ + See base_component.py for detailed description. + + For mobile agents, add state fields for building skill. + """ + if agent_cls_name not in self.agent_subclasses: + return {} + if agent_cls_name == "BasicMobileAgent": + return {"build_payment": float(self.payment), "build_skill": 1} + raise NotImplementedError + + def component_step(self): + """ + See base_component.py for detailed description. + + Convert stone+wood to house+coin for agents that choose to build and can. + """ + world = self.world + build = [] + # Apply any building actions taken by the mobile agents + for agent in world.get_random_order_agents(): + + action = agent.get_component_action(self.name) + + # This component doesn't apply to this agent! + if action is None: + continue + + # NO-OP! + if action == 0: + pass + + # Build! (If you can.) + elif action == 1: + if self.agent_can_build(agent): + # Remove the resources + for resource, cost in self.resource_cost.items(): + agent.state["inventory"][resource] -= cost + + # Place a house where the agent is standing + loc_r, loc_c = agent.loc + world.create_landmark("House", loc_r, loc_c, agent.idx) + + # Receive payment for the house + agent.state["inventory"]["Coin"] += agent.state["build_payment"] + + # Incur the labor cost for building + agent.state["endogenous"]["Labor"] += self.build_labor + + build.append( + { + "builder": agent.idx, + "loc": np.array(agent.loc), + "income": float(agent.state["build_payment"]), + } + ) + + else: + raise ValueError + + self.builds.append(build) + + def generate_observations(self): + """ + See base_component.py for detailed description. + + Here, agents observe their build skill. The planner does not observe anything + from this component. + """ + + obs_dict = dict() + for agent in self.world.agents: + obs_dict[agent.idx] = { + "build_payment": agent.state["build_payment"] / self.payment, + "build_skill": self.sampled_skills[agent.idx], + } + + return obs_dict + + def generate_masks(self, completions=0): + """ + See base_component.py for detailed description. + + Prevent building only if a landmark already occupies the agent's location. + """ + + masks = {} + # Mobile agents' build action is masked if they cannot build with their + # current location and/or endowment + for agent in self.world.agents: + masks[agent.idx] = np.array([self.agent_can_build(agent)]) + + return masks + + # For non-required customization + # ------------------------------ + + def get_metrics(self): + """ + Metrics that capture what happened through this component. + + Returns: + metrics (dict): A dictionary of {"metric_name": metric_value}, + where metric_value is a scalar. + """ + world = self.world + + build_stats = {a.idx: {"n_builds": 0} for a in world.agents} + for builds in self.builds: + for build in builds: + idx = build["builder"] + build_stats[idx]["n_builds"] += 1 + + out_dict = {} + for a in world.agents: + for k, v in build_stats[a.idx].items(): + out_dict["{}/{}".format(a.idx, k)] = v + + num_houses = np.sum(world.maps.get("House") > 0) + out_dict["total_builds"] = num_houses + + return out_dict + + def additional_reset_steps(self): + """ + See base_component.py for detailed description. + + Re-sample agents' building skills. + """ + world = self.world + + self.sampled_skills = {agent.idx: 1 for agent in world.agents} + + PMSM = self.payment_max_skill_multiplier + + for agent in world.agents: + if self.skill_dist == "none": + sampled_skill = 1 + pay_rate = 1 + elif self.skill_dist == "pareto": + sampled_skill = np.random.pareto(4) + pay_rate = np.minimum(PMSM, (PMSM - 1) * sampled_skill + 1) + elif self.skill_dist == "lognormal": + sampled_skill = np.random.lognormal(-1, 0.5) + pay_rate = np.minimum(PMSM, (PMSM - 1) * sampled_skill + 1) + else: + raise NotImplementedError + + agent.state["build_payment"] = float(pay_rate * self.payment) + agent.state["build_skill"] = float(sampled_skill) + + self.sampled_skills[agent.idx] = sampled_skill + + self.builds = [] + + def get_dense_log(self): + """ + Log builds. + + Returns: + builds (list): A list of build events. Each entry corresponds to a single + timestep and contains a description of any builds that occurred on + that timestep. + + """ + return self.builds diff --git a/ai_economist/foundation/components/continuous_double_auction.py b/ai_economist/foundation/components/continuous_double_auction.py new file mode 100644 index 0000000..54bc3e2 --- /dev/null +++ b/ai_economist/foundation/components/continuous_double_auction.py @@ -0,0 +1,679 @@ +# Copyright (c) 2020, salesforce.com, inc. +# All rights reserved. +# SPDX-License-Identifier: BSD-3-Clause +# For full license text, see the LICENSE file in the repo root +# or https://opensource.org/licenses/BSD-3-Clause + +import numpy as np + +from ai_economist.foundation.base.base_component import ( + BaseComponent, + component_registry, +) +from ai_economist.foundation.entities import resource_registry + + +@component_registry.add +class ContinuousDoubleAuction(BaseComponent): + """Allows mobile agents to buy/sell collectible resources with one another. + + Implements a commodity-exchange-style market where agents may sell a unit of + resource by submitting an ask (saying the minimum it will accept in payment) + or may buy a resource by submitting a bid (saying the maximum it will pay in + exchange for a unit of a given resource). + + Args: + max_bid_ask (int): Maximum amount of coin that an agent can bid or ask for. + Must be >= 1. Default is 10 coin. + order_labor (float): Amount of labor incurred when an agent creates an order. + Must be >= 0. Default is 0.25. + order_duration (int): Number of environment timesteps before an unfilled + bid/ask expires. Must be >= 1. Default is 50 timesteps. + max_num_orders (int, optional): Maximum number of bids + asks that an agent can + have open for a given resource. Must be >= 1. Default is no limit to + number of orders. + """ + + name = "ContinuousDoubleAuction" + component_type = "Trade" + required_entities = ["Coin", "Labor"] + agent_subclasses = ["BasicMobileAgent"] + + def __init__( + self, + *args, + max_bid_ask=10, + order_labor=0.25, + order_duration=50, + max_num_orders=None, + **kwargs + ): + super().__init__(*args, **kwargs) + + # The max amount (in coin) that an agent can bid/ask for 1 unit of a commodity + self.max_bid_ask = int(max_bid_ask) + assert self.max_bid_ask >= 1 + self.price_floor = 0 + self.price_ceiling = int(max_bid_ask) + + # The amount of time (in timesteps) that an order stays in the books + # before it expires + self.order_duration = int(order_duration) + assert self.order_duration >= 1 + + # The maximum number of bid+ask orders an agent can have open + # for each type of commodity + self.max_num_orders = int(max_num_orders or self.order_duration) + assert self.max_num_orders >= 1 + + # The labor cost associated with creating a bid or ask order + + self.order_labor = float(order_labor) + self.order_labor = max(self.order_labor, 0.0) + + # Each collectible resource in the world can be traded via this component + self.commodities = [ + r for r in self.world.resources if resource_registry.get(r).collectible + ] + + # These get reset at the start of an episode: + self.asks = {c: [] for c in self.commodities} + self.bids = {c: [] for c in self.commodities} + self.n_orders = { + c: {i: 0 for i in range(self.n_agents)} for c in self.commodities + } + self.executed_trades = [] + self.price_history = { + c: {i: self._price_zeros() for i in range(self.n_agents)} + for c in self.commodities + } + self.bid_hists = { + c: {i: self._price_zeros() for i in range(self.n_agents)} + for c in self.commodities + } + self.ask_hists = { + c: {i: self._price_zeros() for i in range(self.n_agents)} + for c in self.commodities + } + + # Convenience methods + # ------------------- + + def _price_zeros(self): + if 1 + self.price_ceiling - self.price_floor <= 0: + print("ERROR!", self.price_ceiling, self.price_floor) + + return np.zeros(1 + self.price_ceiling - self.price_floor) + + def available_asks(self, resource, agent): + """ + Get a histogram of asks for resource to which agent could bid against. + + Args: + resource (str): Name of the resource + agent (BasicMobileAgent or None): Object of agent for which available + asks are being queried. If None, all asks are considered available. + + Returns: + ask_hist (ndarray): For each possible price level, the number of + available asks. + """ + if agent is None: + a_idx = -1 + else: + a_idx = agent.idx + ask_hist = self._price_zeros() + for i, h in self.ask_hists[resource].items(): + if a_idx != i: + ask_hist += h + return ask_hist + + def available_bids(self, resource, agent): + """ + Get a histogram of bids for resource to which agent could ask against. + + Args: + resource (str): Name of the resource + agent (BasicMobileAgent or None): Object of agent for which available + bids are being queried. If None, all bids are considered available. + + Returns: + bid_hist (ndarray): For each possible price level, the number of + available bids. + """ + if agent is None: + a_idx = -1 + else: + a_idx = agent.idx + bid_hist = self._price_zeros() + for i, h in self.bid_hists[resource].items(): + if a_idx != i: + bid_hist += h + return bid_hist + + def can_bid(self, resource, agent): + """If agent can submit a bid for resource.""" + return self.n_orders[resource][agent.idx] < self.max_num_orders + + def can_ask(self, resource, agent): + """If agent can submit an ask for resource.""" + return ( + self.n_orders[resource][agent.idx] < self.max_num_orders + and agent.state["inventory"][resource] > 0 + ) + + # Core components for this market + # ------------------------------- + + def create_bid(self, resource, agent, max_payment): + """Create a new bid for resource, with agent offering max_payment. + + On a successful trade, payment will be at most max_payment, possibly less. + + The agent places the bid coin into escrow so that it may not be spent on + something else while the order exists. + """ + + # The agent is past the max number of orders + # or doesn't have enough money, do nothing + if (not self.can_bid(resource, agent)) or agent.state["inventory"][ + "Coin" + ] < max_payment: + return + + assert self.price_floor <= max_payment <= self.price_ceiling + + bid = {"buyer": agent.idx, "bid": int(max_payment), "bid_lifetime": 0} + + # Add this to the bid book + self.bids[resource].append(bid) + self.bid_hists[resource][bid["buyer"]][bid["bid"] - self.price_floor] += 1 + self.n_orders[resource][agent.idx] += 1 + + # Set aside whatever money the agent is willing to pay + # (will get excess back if price ends up being less) + _ = agent.inventory_to_escrow("Coin", int(max_payment)) + + # Incur the labor cost of creating an order + agent.state["endogenous"]["Labor"] += self.order_labor + + def create_ask(self, resource, agent, min_income): + """ + Create a new ask for resource, with agent asking for min_income. + + On a successful trade, income will be at least min_income, possibly more. + + The agent places one unit of resource into escrow so that it may not be used + for something else while the order exists. + """ + # The agent is past the max number of orders + # or doesn't the resource it's trying to sell, do nothing + if not self.can_ask(resource, agent): + return + + # is there an upper limit? + assert self.price_floor <= min_income <= self.price_ceiling + + ask = {"seller": agent.idx, "ask": int(min_income), "ask_lifetime": 0} + + # Add this to the ask book + self.asks[resource].append(ask) + self.ask_hists[resource][ask["seller"]][ask["ask"] - self.price_floor] += 1 + self.n_orders[resource][agent.idx] += 1 + + # Set aside the resource the agent is willing to sell + amount = agent.inventory_to_escrow(resource, 1) + assert amount == 1 + + # Incur the labor cost of creating an order + agent.state["endogenous"]["Labor"] += self.order_labor + + def match_orders(self): + """ + This implements the continuous double auction by identifying valid bid/ask + pairs and executing trades accordingly. + + Higher (lower) bids (asks) are given priority over lower (higher) bids (asks). + Trades are executed using the price of whichever bid/ask order was placed + first: bid price if bid was placed first, ask price otherwise. + + Trading removes the payment and resource from bidder's and asker's escrow, + respectively, and puts them in the other's inventory. + """ + self.executed_trades.append([]) + + for resource in self.commodities: + possible_match = [True for _ in range(self.n_agents)] + keep_checking = True + + bids = sorted( + self.bids[resource], + key=lambda b: (b["bid"], b["bid_lifetime"]), + reverse=True, + ) + asks = sorted( + self.asks[resource], key=lambda a: (a["ask"], -a["ask_lifetime"]) + ) + + while any(possible_match) and keep_checking: + idx_bid, idx_ask = 0, 0 + while True: + # Out of bids to check. Exit both loops. + if idx_bid >= len(bids): + keep_checking = False + break + + # Already know this buyer is no good for this round. + # Skip to next bid. + if not possible_match[bids[idx_bid]["buyer"]]: + idx_bid += 1 + + # Out of asks to check. This buyer won't find a match on this round. + # (maybe) Restart inner loop. + elif idx_ask >= len(asks): + possible_match[bids[idx_bid]["buyer"]] = False + break + + # Skip to next ask if this ask comes from the buyer + # of the current bid. + elif asks[idx_ask]["seller"] == bids[idx_bid]["buyer"]: + idx_ask += 1 + + # If this bid/ask pair can't be matched, this buyer + # can't be matched. (maybe) Restart inner loop. + elif bids[idx_bid]["bid"] < asks[idx_ask]["ask"]: + possible_match[bids[idx_bid]["buyer"]] = False + break + + # TRADE! (then restart inner loop) + else: + bid = bids.pop(idx_bid) + ask = asks.pop(idx_ask) + + trade = {"commodity": resource} + trade.update(bid) + trade.update(ask) + + if ( + bid["bid_lifetime"] <= ask["ask_lifetime"] + ): # Ask came earlier. (in other words, + # trade triggered by new bid) + trade["price"] = int(trade["ask"]) + else: # Bid came earlier. (in other words, + # trade triggered by new ask) + trade["price"] = int(trade["bid"]) + trade["cost"] = trade["price"] # What the buyer pays in total + trade["income"] = trade[ + "price" + ] # What the seller receives in total + + buyer = self.world.agents[trade["buyer"]] + seller = self.world.agents[trade["seller"]] + + # Bookkeeping + self.bid_hists[resource][bid["buyer"]][ + bid["bid"] - self.price_floor + ] -= 1 + self.ask_hists[resource][ask["seller"]][ + ask["ask"] - self.price_floor + ] -= 1 + self.n_orders[trade["commodity"]][seller.idx] -= 1 + self.n_orders[trade["commodity"]][buyer.idx] -= 1 + self.executed_trades[-1].append(trade) + self.price_history[resource][trade["seller"]][ + trade["price"] + ] += 1 + + # The resource goes from the seller's escrow + # to the buyer's inventory + seller.state["escrow"][resource] -= 1 + buyer.state["inventory"][resource] += 1 + + # Buyer's money (already set aside) leaves escrow + pre_payment = int(trade["bid"]) + buyer.state["escrow"]["Coin"] -= pre_payment + assert buyer.state["escrow"]["Coin"] >= 0 + + # Payment is removed from the pre_payment + # and given to the seller. Excess returned to buyer. + payment_to_seller = int(trade["price"]) + excess_payment_from_buyer = pre_payment - payment_to_seller + assert excess_payment_from_buyer >= 0 + seller.state["inventory"]["Coin"] += payment_to_seller + buyer.state["inventory"]["Coin"] += excess_payment_from_buyer + + # Restart the inner loop + break + + # Keep the unfilled bids/asks + self.bids[resource] = bids + self.asks[resource] = asks + + def remove_expired_orders(self): + """ + Increment the time counter for any unfilled bids/asks and remove expired + orders from the market. + + When orders expire, the payment or resource is removed from escrow and + returned to the inventory and the associated order is removed from the order + books. + """ + world = self.world + + for resource in self.commodities: + + bids_ = [] + for bid in self.bids[resource]: + bid["bid_lifetime"] += 1 + # If the bid is not expired, keep it in the bids + if bid["bid_lifetime"] <= self.order_duration: + bids_.append(bid) + # Otherwise, remove it and do the associated bookkeeping + else: + # Return the set aside money to the buyer + amount = world.agents[bid["buyer"]].escrow_to_inventory( + "Coin", bid["bid"] + ) + assert amount == bid["bid"] + # Adjust the bid histogram to reflect the removal of the bid + self.bid_hists[resource][bid["buyer"]][ + bid["bid"] - self.price_floor + ] -= 1 + # Adjust the order counter + self.n_orders[resource][bid["buyer"]] -= 1 + + asks_ = [] + for ask in self.asks[resource]: + ask["ask_lifetime"] += 1 + # If the ask is not expired, keep it in the asks + if ask["ask_lifetime"] <= self.order_duration: + asks_.append(ask) + # Otherwise, remove it and do the associated bookkeeping + else: + # Return the set aside resource to the seller + resource_unit = world.agents[ask["seller"]].escrow_to_inventory( + resource, 1 + ) + assert resource_unit == 1 + # Adjust the ask histogram to reflect the removal of the ask + self.ask_hists[resource][ask["seller"]][ + ask["ask"] - self.price_floor + ] -= 1 + # Adjust the order counter + self.n_orders[resource][ask["seller"]] -= 1 + + self.bids[resource] = bids_ + self.asks[resource] = asks_ + + # Required methods for implementing components + # -------------------------------------------- + + def get_n_actions(self, agent_cls_name): + """ + See base_component.py for detailed description. + + Adds 2*C action spaces [ (bid+ask) * n_commodities ], each with 1 + max_bid_ask + actions corresponding to price levels 0 to max_bid_ask. + """ + # This component adds 2*(1+max_bid_ask)*n_resources possible actions: + # buy/sell x each-price x each-resource + if agent_cls_name == "BasicMobileAgent": + trades = [] + for c in self.commodities: + trades.append( + ("Buy_{}".format(c), 1 + self.max_bid_ask) + ) # How much willing to pay for c + trades.append( + ("Sell_{}".format(c), 1 + self.max_bid_ask) + ) # How much need to receive to sell c + return trades + + return None + + def get_additional_state_fields(self, agent_cls_name): + """ + See base_component.py for detailed description. + """ + # This component doesn't add any state fields + return {} + + def component_step(self): + """ + See base_component.py for detailed description. + + Create new bids and asks, match and execute valid order pairs, and manage + order expiration. + """ + world = self.world + + for resource in self.commodities: + for agent in world.agents: + self.price_history[resource][agent.idx] *= 0.995 + + # Create bid action + # ----------------- + resource_action = agent.get_component_action( + self.name, "Buy_{}".format(resource) + ) + + # No-op + if resource_action == 0: + pass + + # Create a bid + elif resource_action <= self.max_bid_ask + 1: + self.create_bid(resource, agent, max_payment=resource_action - 1) + + else: + raise ValueError + + # Create ask action + # ----------------- + resource_action = agent.get_component_action( + self.name, "Sell_{}".format(resource) + ) + + # No-op + if resource_action == 0: + pass + + # Create an ask + elif resource_action <= self.max_bid_ask + 1: + self.create_ask(resource, agent, min_income=resource_action - 1) + + else: + raise ValueError + + # Here's where the magic happens: + self.match_orders() # Pair bids and asks + self.remove_expired_orders() # Get rid of orders that have expired + + def generate_observations(self): + """ + See base_component.py for detailed description. + + Here, agents and the planner both observe historical market behavior and + outstanding bids/asks for each tradable commodity. Agents only see the + outstanding bids/asks to which they could respond (that is, that they did not + submit). Agents also see their own outstanding bids/asks. + """ + world = self.world + + obs = {a.idx: {} for a in world.agents + [world.planner]} + + prices = np.arange(self.price_floor, self.price_ceiling + 1) + for c in self.commodities: + net_price_history = np.sum( + np.stack([self.price_history[c][i] for i in range(self.n_agents)]), + axis=0, + ) + market_rate = prices.dot(net_price_history) / np.maximum( + 0.001, np.sum(net_price_history) + ) + scaled_price_history = net_price_history * self.inv_scale + + full_asks = self.available_asks(c, agent=None) + full_bids = self.available_bids(c, agent=None) + + obs[world.planner.idx].update( + { + "market_rate-{}".format(c): market_rate, + "price_history-{}".format(c): scaled_price_history, + "full_asks-{}".format(c): full_asks, + "full_bids-{}".format(c): full_bids, + } + ) + + for _, agent in enumerate(world.agents): + # Private to the agent + obs[agent.idx].update( + { + "market_rate-{}".format(c): market_rate, + "price_history-{}".format(c): scaled_price_history, + "available_asks-{}".format(c): full_asks + - self.ask_hists[c][agent.idx], + "available_bids-{}".format(c): full_bids + - self.bid_hists[c][agent.idx], + "my_asks-{}".format(c): self.ask_hists[c][agent.idx], + "my_bids-{}".format(c): self.bid_hists[c][agent.idx], + } + ) + + return obs + + def generate_masks(self, completions=0): + """ + See base_component.py for detailed description. + + Agents cannot submit bids/asks for resources where they are at the order + limit. In addition, they may only submit asks for resources they possess and + bids for which they can pay. + """ + world = self.world + + masks = dict() + + for agent in world.agents: + masks[agent.idx] = {} + + can_pay = np.arange(self.max_bid_ask + 1) <= agent.inventory["Coin"] + + for resource in self.commodities: + if not self.can_ask(resource, agent): # asks_maxed: + masks[agent.idx]["Sell_{}".format(resource)] = np.zeros( + 1 + self.max_bid_ask + ) + else: + masks[agent.idx]["Sell_{}".format(resource)] = np.ones( + 1 + self.max_bid_ask + ) + + if not self.can_bid(resource, agent): + masks[agent.idx]["Buy_{}".format(resource)] = np.zeros( + 1 + self.max_bid_ask + ) + else: + masks[agent.idx]["Buy_{}".format(resource)] = can_pay.astype( + np.int32 + ) + + return masks + + # For non-required customization + # ------------------------------ + + def get_metrics(self): + """ + Metrics that capture what happened through this component. + + Returns: + metrics (dict): A dictionary of {"metric_name": metric_value}, + where metric_value is a scalar. + """ + world = self.world + + trade_keys = ["price", "cost", "income"] + + selling_stats = { + a.idx: { + c: {k: 0 for k in trade_keys + ["n_sales"]} for c in self.commodities + } + for a in world.agents + } + buying_stats = { + a.idx: { + c: {k: 0 for k in trade_keys + ["n_sales"]} for c in self.commodities + } + for a in world.agents + } + + n_trades = 0 + + for trades in self.executed_trades: + for trade in trades: + n_trades += 1 + i_s, i_b, c = trade["seller"], trade["buyer"], trade["commodity"] + selling_stats[i_s][c]["n_sales"] += 1 + buying_stats[i_b][c]["n_sales"] += 1 + for k in trade_keys: + selling_stats[i_s][c][k] += trade[k] + buying_stats[i_b][c][k] += trade[k] + + out_dict = {} + for a in world.agents: + for c in self.commodities: + for stats, prefix in zip( + [selling_stats, buying_stats], ["Sell", "Buy"] + ): + n = stats[a.idx][c]["n_sales"] + if n == 0: + for k in trade_keys: + stats[a.idx][c][k] = np.nan + else: + for k in trade_keys: + stats[a.idx][c][k] /= n + + for k, v in stats[a.idx][c].items(): + out_dict["{}/{}{}/{}".format(a.idx, prefix, c, k)] = v + + out_dict["n_trades"] = n_trades + + return out_dict + + def additional_reset_steps(self): + """ + See base_component.py for detailed description. + + Reset the order books. + """ + self.bids = {c: [] for c in self.commodities} + self.asks = {c: [] for c in self.commodities} + self.n_orders = { + c: {i: 0 for i in range(self.n_agents)} for c in self.commodities + } + + self.price_history = { + c: {i: self._price_zeros() for i in range(self.n_agents)} + for c in self.commodities + } + self.bid_hists = { + c: {i: self._price_zeros() for i in range(self.n_agents)} + for c in self.commodities + } + self.ask_hists = { + c: {i: self._price_zeros() for i in range(self.n_agents)} + for c in self.commodities + } + + self.executed_trades = [] + + def get_dense_log(self): + """ + Log executed trades. + + Returns: + trades (list): A list of trade events. Each entry corresponds to a single + timestep and contains a description of any trades that occurred on + that timestep. + """ + return self.executed_trades diff --git a/ai_economist/foundation/components/covid19_components.py b/ai_economist/foundation/components/covid19_components.py new file mode 100644 index 0000000..0a47c8c --- /dev/null +++ b/ai_economist/foundation/components/covid19_components.py @@ -0,0 +1,663 @@ +# Copyright (c) 2021, salesforce.com, inc. +# All rights reserved. +# SPDX-License-Identifier: BSD-3-Clause +# For full license text, see the LICENSE file in the repo root +# or https://opensource.org/licenses/BSD-3-Clause + +from datetime import datetime + +import GPUtil +import numpy as np + +from ai_economist.foundation.base.base_component import ( + BaseComponent, + component_registry, +) + +try: + num_gpus_available = len(GPUtil.getAvailable()) + print(f"Inside covid19_components.py: {num_gpus_available} GPUs are available.") + if num_gpus_available == 0: + print("No GPUs found! Running the simulation on a CPU.") + else: + from warp_drive.utils.constants import Constants + from warp_drive.utils.data_feed import DataFeed + + _OBSERVATIONS = Constants.OBSERVATIONS + _ACTIONS = Constants.ACTIONS +except ModuleNotFoundError: + print( + "Warning: The 'WarpDrive' package is not found and cannot be used! " + "If you wish to use WarpDrive, please run " + "'pip install rl-warp-drive' first." + ) +except ValueError: + print("No GPUs found! Running the simulation on a CPU.") + + +@component_registry.add +class ControlUSStateOpenCloseStatus(BaseComponent): + """ + Sets the open/close stringency levels for states. + Args: + n_stringency_levels (int): number of stringency levels the states can chose + from. (Must match the number in the model constants dictionary referenced by + the parent scenario.) + action_cooldown_period (int): action cooldown period in days. + Once a stringency level is set, the state(s) cannot switch to another level + for a certain number of days (referred to as the "action_cooldown_period") + """ + + name = "ControlUSStateOpenCloseStatus" + required_entities = [] + agent_subclasses = ["BasicMobileAgent"] + + def __init__( + self, + *base_component_args, + n_stringency_levels=10, + action_cooldown_period=28, + **base_component_kwargs, + ): + + self.action_cooldown_period = action_cooldown_period + super().__init__(*base_component_args, **base_component_kwargs) + self.np_int_dtype = np.int32 + + self.n_stringency_levels = int(n_stringency_levels) + assert self.n_stringency_levels >= 2 + self._checked_n_stringency_levels = False + + self.masks = dict() + self.default_agent_action_mask = [1 for _ in range(self.n_stringency_levels)] + self.no_op_agent_action_mask = [0 for _ in range(self.n_stringency_levels)] + self.masks["a"] = np.repeat( + np.array(self.no_op_agent_action_mask)[:, np.newaxis], + self.n_agents, + axis=-1, + ) + + # (This will be overwritten during reset; see below) + self.action_in_cooldown_until = None + + def get_additional_state_fields(self, agent_cls_name): + return {} + + def additional_reset_steps(self): + # Store the times when the next set of actions can be taken. + self.action_in_cooldown_until = np.array( + [self.world.timestep for _ in range(self.n_agents)] + ) + + def get_n_actions(self, agent_cls_name): + if agent_cls_name == "BasicMobileAgent": + return self.n_stringency_levels + return None + + def generate_masks(self, completions=0): + for agent in self.world.agents: + if self.world.use_real_world_policies: + self.masks["a"][:, agent.idx] = self.default_agent_action_mask + else: + if self.world.timestep < self.action_in_cooldown_until[agent.idx]: + # Keep masking the actions + self.masks["a"][:, agent.idx] = self.no_op_agent_action_mask + else: # self.world.timestep == self.action_in_cooldown_until[agent.idx] + # Cooldown period has ended; unmask the "subsequent" action + self.masks["a"][:, agent.idx] = self.default_agent_action_mask + return self.masks + + def get_data_dictionary(self): + """ + Create a dictionary of data to push to the GPU (device). + """ + data_dict = DataFeed() + data_dict.add_data( + name="action_cooldown_period", + data=self.action_cooldown_period, + ) + data_dict.add_data( + name="action_in_cooldown_until", + data=self.action_in_cooldown_until, + save_copy_and_apply_at_reset=True, + ) + data_dict.add_data( + name="num_stringency_levels", + data=self.n_stringency_levels, + ) + data_dict.add_data( + name="default_agent_action_mask", + data=[1] + self.default_agent_action_mask, + ) + data_dict.add_data( + name="no_op_agent_action_mask", + data=[1] + self.no_op_agent_action_mask, + ) + return data_dict + + def get_tensor_dictionary(self): + """ + Create a dictionary of (Pytorch-accessible) data to push to the GPU (device). + """ + tensor_dict = DataFeed() + return tensor_dict + + def component_step(self): + if self.world.use_cuda: + self.world.cuda_component_step[self.name]( + self.world.cuda_data_manager.device_data("stringency_level"), + self.world.cuda_data_manager.device_data("action_cooldown_period"), + self.world.cuda_data_manager.device_data("action_in_cooldown_until"), + self.world.cuda_data_manager.device_data("default_agent_action_mask"), + self.world.cuda_data_manager.device_data("no_op_agent_action_mask"), + self.world.cuda_data_manager.device_data("num_stringency_levels"), + self.world.cuda_data_manager.device_data(f"{_ACTIONS}_a"), + self.world.cuda_data_manager.device_data( + f"{_OBSERVATIONS}_a_{self.name}-agent_policy_indicators" + ), + self.world.cuda_data_manager.device_data( + f"{_OBSERVATIONS}_a_action_mask" + ), + self.world.cuda_data_manager.device_data( + f"{_OBSERVATIONS}_p_{self.name}-agent_policy_indicators" + ), + self.world.cuda_data_manager.device_data("_timestep_"), + self.world.cuda_data_manager.meta_info("n_agents"), + self.world.cuda_data_manager.meta_info("episode_length"), + block=self.world.cuda_function_manager.block, + grid=self.world.cuda_function_manager.grid, + ) + else: + if not self._checked_n_stringency_levels: + if self.n_stringency_levels != self.world.n_stringency_levels: + raise ValueError( + "The environment was not configured correctly. For the given " + "model fit, you need to set the number of stringency levels to " + "be {}".format(self.world.n_stringency_levels) + ) + self._checked_n_stringency_levels = True + + for agent in self.world.agents: + if self.world.use_real_world_policies: + # Use the action taken in the previous timestep + action = self.world.real_world_stringency_policy[ + self.world.timestep - 1, agent.idx + ] + else: + action = agent.get_component_action(self.name) + assert 0 <= action <= self.n_stringency_levels + + # We only update the stringency level if the action is not a NO-OP. + self.world.global_state["Stringency Level"][ + self.world.timestep, agent.idx + ] = ( + self.world.global_state["Stringency Level"][ + self.world.timestep - 1, agent.idx + ] + * (action == 0) + + action + ) + + agent.state[ + "Current Open Close Stringency Level" + ] = self.world.global_state["Stringency Level"][ + self.world.timestep, agent.idx + ] + + # Check if the action cooldown period has ended, and set the next + # time until action cooldown. If current action is a no-op + # (i.e., no new action was taken), the agent can take an action + # in the very next step, otherwise it needs to wait for + # self.action_cooldown_period steps. When in the action cooldown + # period, whatever actions the agents take are masked out, + # so it's always a NO-OP (see generate_masks() above) + # The logic below influences the action masks. + if self.world.timestep == self.action_in_cooldown_until[agent.idx] + 1: + if action == 0: # NO-OP + self.action_in_cooldown_until[agent.idx] += 1 + else: + self.action_in_cooldown_until[ + agent.idx + ] += self.action_cooldown_period + + def generate_observations(self): + + # Normalized observations + obs_dict = dict() + agent_policy_indicators = self.world.global_state["Stringency Level"][ + self.world.timestep + ] + obs_dict["a"] = { + "agent_policy_indicators": agent_policy_indicators + / self.n_stringency_levels + } + obs_dict[self.world.planner.idx] = { + "agent_policy_indicators": agent_policy_indicators + / self.n_stringency_levels + } + + return obs_dict + + +@component_registry.add +class FederalGovernmentSubsidy(BaseComponent): + """ + Args: + subsidy_interval (int): The number of days over which the total subsidy amount + is evenly rolled out. + Note: shortening the subsidy interval increases the total amount of money + that the planner could possibly spend. For instance, if the subsidy + interval is 30, the planner can create a subsidy every 30 days. + num_subsidy_levels (int): The number of subsidy levels. + Note: with max_annual_subsidy_per_person=10000, one round of subsidies at + the maximum subsidy level equals an expenditure of roughly $3.3 trillion + (given the US population of 330 million). + If the planner chooses the maximum subsidy amount, the $3.3 trillion + is rolled out gradually over the subsidy interval. + max_annual_subsidy_per_person (float): The maximum annual subsidy that may be + allocated per person. + """ + + name = "FederalGovernmentSubsidy" + required_entities = [] + agent_subclasses = ["BasicPlanner"] + + def __init__( + self, + *base_component_args, + subsidy_interval=90, + num_subsidy_levels=20, + max_annual_subsidy_per_person=20000, + **base_component_kwargs, + ): + self.subsidy_interval = int(subsidy_interval) + assert self.subsidy_interval >= 1 + + self.num_subsidy_levels = int(num_subsidy_levels) + assert self.num_subsidy_levels >= 1 + + self.max_annual_subsidy_per_person = float(max_annual_subsidy_per_person) + assert self.max_annual_subsidy_per_person >= 0 + + self.np_int_dtype = np.int32 + + # (This will be overwritten during component_step; see below) + self._subsidy_amount_per_level = None + self._subsidy_level_array = None + + super().__init__(*base_component_args, **base_component_kwargs) + + self.default_planner_action_mask = [1 for _ in range(self.num_subsidy_levels)] + self.no_op_planner_action_mask = [0 for _ in range(self.num_subsidy_levels)] + + # (This will be overwritten during reset; see below) + self.max_daily_subsidy_per_state = np.array( + self.n_agents, dtype=self.np_int_dtype + ) + + def get_additional_state_fields(self, agent_cls_name): + if agent_cls_name == "BasicPlanner": + return {"Total Subsidy": 0, "Current Subsidy Level": 0} + return {} + + def additional_reset_steps(self): + # Pre-compute maximum state-specific subsidy levels + self.max_daily_subsidy_per_state = ( + self.world.us_state_population * self.max_annual_subsidy_per_person / 365 + ) + + def get_n_actions(self, agent_cls_name): + if agent_cls_name == "BasicPlanner": + # Number of non-zero subsidy levels + # (the action 0 pertains to the no-subsidy case) + return self.num_subsidy_levels + return None + + def generate_masks(self, completions=0): + masks = {} + if self.world.use_real_world_policies: + masks[self.world.planner.idx] = self.default_planner_action_mask + else: + if self.world.timestep % self.subsidy_interval == 0: + masks[self.world.planner.idx] = self.default_planner_action_mask + else: + masks[self.world.planner.idx] = self.no_op_planner_action_mask + return masks + + def get_data_dictionary(self): + """ + Create a dictionary of data to push to the device + """ + data_dict = DataFeed() + data_dict.add_data( + name="subsidy_interval", + data=self.subsidy_interval, + ) + data_dict.add_data( + name="num_subsidy_levels", + data=self.num_subsidy_levels, + ) + data_dict.add_data( + name="max_daily_subsidy_per_state", + data=self.max_daily_subsidy_per_state, + ) + data_dict.add_data( + name="default_planner_action_mask", + data=[1] + self.default_planner_action_mask, + ) + data_dict.add_data( + name="no_op_planner_action_mask", + data=[1] + self.no_op_planner_action_mask, + ) + return data_dict + + def get_tensor_dictionary(self): + """ + Create a dictionary of (Pytorch-accessible) data to push to the device + """ + tensor_dict = DataFeed() + return tensor_dict + + def component_step(self): + if self.world.use_cuda: + self.world.cuda_component_step[self.name]( + self.world.cuda_data_manager.device_data("subsidy_level"), + self.world.cuda_data_manager.device_data("subsidy"), + self.world.cuda_data_manager.device_data("subsidy_interval"), + self.world.cuda_data_manager.device_data("num_subsidy_levels"), + self.world.cuda_data_manager.device_data("max_daily_subsidy_per_state"), + self.world.cuda_data_manager.device_data("default_planner_action_mask"), + self.world.cuda_data_manager.device_data("no_op_planner_action_mask"), + self.world.cuda_data_manager.device_data(f"{_ACTIONS}_p"), + self.world.cuda_data_manager.device_data( + f"{_OBSERVATIONS}_a_{self.name}-t_until_next_subsidy" + ), + self.world.cuda_data_manager.device_data( + f"{_OBSERVATIONS}_a_{self.name}-current_subsidy_level" + ), + self.world.cuda_data_manager.device_data( + f"{_OBSERVATIONS}_p_{self.name}-t_until_next_subsidy" + ), + self.world.cuda_data_manager.device_data( + f"{_OBSERVATIONS}_p_{self.name}-current_subsidy_level" + ), + self.world.cuda_data_manager.device_data( + f"{_OBSERVATIONS}_p_action_mask" + ), + self.world.cuda_data_manager.device_data("_timestep_"), + self.world.cuda_data_manager.meta_info("n_agents"), + self.world.cuda_data_manager.meta_info("episode_length"), + block=self.world.cuda_function_manager.block, + grid=self.world.cuda_function_manager.grid, + ) + else: + if self.world.use_real_world_policies: + if self._subsidy_amount_per_level is None: + self._subsidy_amount_per_level = ( + self.world.us_population + * self.max_annual_subsidy_per_person + / self.num_subsidy_levels + * self.subsidy_interval + / 365 + ) + self._subsidy_level_array = np.zeros((self._episode_length + 1)) + # Use the action taken in the previous timestep + current_subsidy_amount = self.world.real_world_subsidy[ + self.world.timestep - 1 + ] + if current_subsidy_amount > 0: + _subsidy_level = np.round( + (current_subsidy_amount / self._subsidy_amount_per_level) + ) + for t_idx in range( + self.world.timestep - 1, + min( + len(self._subsidy_level_array), + self.world.timestep - 1 + self.subsidy_interval, + ), + ): + self._subsidy_level_array[t_idx] += _subsidy_level + subsidy_level = self._subsidy_level_array[self.world.timestep - 1] + else: + # Update the subsidy level only every self.subsidy_interval, since the + # other actions are masked out. + if (self.world.timestep - 1) % self.subsidy_interval == 0: + subsidy_level = self.world.planner.get_component_action(self.name) + else: + subsidy_level = self.world.planner.state["Current Subsidy Level"] + + assert 0 <= subsidy_level <= self.num_subsidy_levels + self.world.planner.state["Current Subsidy Level"] = np.array( + subsidy_level + ).astype(self.np_int_dtype) + + # Update subsidy level + subsidy_level_frac = subsidy_level / self.num_subsidy_levels + daily_statewise_subsidy = ( + subsidy_level_frac * self.max_daily_subsidy_per_state + ) + + self.world.global_state["Subsidy"][ + self.world.timestep + ] = daily_statewise_subsidy + self.world.planner.state["Total Subsidy"] += np.sum(daily_statewise_subsidy) + + def generate_observations(self): + # Allow the agents/planner to know when the next subsidy might come. + # Obs should = 0 when the next timestep could include a subsidy + t_since_last_subsidy = self.world.timestep % self.subsidy_interval + # (this is normalized to 0<-->1) + t_until_next_subsidy = self.subsidy_interval - t_since_last_subsidy + t_vec = t_until_next_subsidy * np.ones(self.n_agents) + + current_subsidy_level = self.world.planner.state["Current Subsidy Level"] + sl_vec = current_subsidy_level * np.ones(self.n_agents) + + # Normalized observations + obs_dict = dict() + obs_dict["a"] = { + "t_until_next_subsidy": t_vec / self.subsidy_interval, + "current_subsidy_level": sl_vec / self.num_subsidy_levels, + } + obs_dict[self.world.planner.idx] = { + "t_until_next_subsidy": t_until_next_subsidy / self.subsidy_interval, + "current_subsidy_level": current_subsidy_level / self.num_subsidy_levels, + } + + return obs_dict + + +@component_registry.add +class VaccinationCampaign(BaseComponent): + """ + Implements a (passive) component for delivering vaccines to agents once a certain + amount of time has elapsed. + + Args: + daily_vaccines_per_million_people (int): The number of vaccines available per + million people everyday. + delivery_interval (int): The number of days between vaccine deliveries. + vaccine_delivery_start_date (string): The date (YYYY-MM-DD) when the + vaccination begins. + """ + + name = "VaccinationCampaign" + required_entities = [] + agent_subclasses = ["BasicMobileAgent"] + + def __init__( + self, + *base_component_args, + daily_vaccines_per_million_people=4500, + delivery_interval=1, + vaccine_delivery_start_date="2020-12-22", + observe_rate=False, + **base_component_kwargs, + ): + self.daily_vaccines_per_million_people = int(daily_vaccines_per_million_people) + assert 0 <= self.daily_vaccines_per_million_people <= 1e6 + + self.delivery_interval = int(delivery_interval) + assert 1 <= self.delivery_interval <= 5000 + + try: + self.vaccine_delivery_start_date = datetime.strptime( + vaccine_delivery_start_date, "%Y-%m-%d" + ) + except ValueError: + print("Incorrect data format, should be YYYY-MM-DD") + + # (This will be overwritten during component_step (see below)) + self._time_when_vaccine_delivery_begins = None + + self.np_int_dtype = np.int32 + + self.observe_rate = bool(observe_rate) + + super().__init__(*base_component_args, **base_component_kwargs) + + # (This will be overwritten during reset; see below) + self._num_vaccines_per_delivery = None + # Convenience for obs (see usage below): + self._t_first_delivery = None + + @property + def num_vaccines_per_delivery(self): + if self._num_vaccines_per_delivery is None: + # Pre-compute dispersal numbers + millions_of_residents = self.world.us_state_population / 1e6 + daily_vaccines = ( + millions_of_residents * self.daily_vaccines_per_million_people + ) + num_vaccines_per_delivery = np.floor( + self.delivery_interval * daily_vaccines + ) + self._num_vaccines_per_delivery = np.array( + num_vaccines_per_delivery, dtype=self.np_int_dtype + ) + return self._num_vaccines_per_delivery + + @property + def time_when_vaccine_delivery_begins(self): + if self._time_when_vaccine_delivery_begins is None: + self._time_when_vaccine_delivery_begins = ( + self.vaccine_delivery_start_date - self.world.start_date + ).days + return self._time_when_vaccine_delivery_begins + + def get_additional_state_fields(self, agent_cls_name): + if agent_cls_name == "BasicMobileAgent": + return {"Total Vaccinated": 0, "Vaccines Available": 0} + return {} + + def additional_reset_steps(self): + pass + + def get_n_actions(self, agent_cls_name): + return # Passive component + + def generate_masks(self, completions=0): + return {} # Passive component + + def get_data_dictionary(self): + """ + Create a dictionary of data to push to the device + """ + data_dict = DataFeed() + data_dict.add_data( + name="num_vaccines_per_delivery", + data=self.num_vaccines_per_delivery, + ) + data_dict.add_data( + name="delivery_interval", + data=self.delivery_interval, + ) + data_dict.add_data( + name="time_when_vaccine_delivery_begins", + data=self.time_when_vaccine_delivery_begins, + ) + data_dict.add_data( + name="num_vaccines_available_t", + data=np.zeros(self.n_agents), + save_copy_and_apply_at_reset=True, + ) + return data_dict + + def get_tensor_dictionary(self): + """ + Create a dictionary of (Pytorch-accessible) data to push to the device + """ + tensor_dict = DataFeed() + return tensor_dict + + def component_step(self): + if self.world.use_cuda: + self.world.cuda_component_step[self.name]( + self.world.cuda_data_manager.device_data("vaccinated"), + self.world.cuda_data_manager.device_data("num_vaccines_per_delivery"), + self.world.cuda_data_manager.device_data("num_vaccines_available_t"), + self.world.cuda_data_manager.device_data("delivery_interval"), + self.world.cuda_data_manager.device_data( + "time_when_vaccine_delivery_begins" + ), + self.world.cuda_data_manager.device_data( + f"{_OBSERVATIONS}_a_{self.name}-t_until_next_vaccines" + ), + self.world.cuda_data_manager.device_data( + f"{_OBSERVATIONS}_p_{self.name}-t_until_next_vaccines" + ), + self.world.cuda_data_manager.device_data("_timestep_"), + self.world.cuda_data_manager.meta_info("n_agents"), + self.world.cuda_data_manager.meta_info("episode_length"), + block=self.world.cuda_function_manager.block, + grid=self.world.cuda_function_manager.grid, + ) + else: + # Do nothing if vaccines are not available yet + if self.world.timestep < self.time_when_vaccine_delivery_begins: + return + + # Do nothing if this is not the start of a delivery interval. + # Vaccines are delivered at the start of each interval. + if (self.world.timestep % self.delivery_interval) != 0: + return + + # Deliver vaccines to each state + for aidx, vaccines in enumerate(self.num_vaccines_per_delivery): + self.world.agents[aidx].state["Vaccines Available"] += vaccines + + def generate_observations(self): + # Allow the agents/planner to know when the next vaccines might come. + # Obs should = 0 when the next timestep will deliver vaccines + # (this is normalized to 0<-->1) + + if self._t_first_delivery is None: + self._t_first_delivery = int(self.time_when_vaccine_delivery_begins) + while (self._t_first_delivery % self.delivery_interval) != 0: + self._t_first_delivery += 1 + + next_t = self.world.timestep + 1 + if next_t <= self._t_first_delivery: + t_until_next_vac = np.minimum( + 1, (self._t_first_delivery - next_t) / self.delivery_interval + ) + next_vax_rate = 0.0 + else: + t_since_last_vac = next_t % self.delivery_interval + t_until_next_vac = self.delivery_interval - t_since_last_vac + next_vax_rate = self.daily_vaccines_per_million_people / 1e6 + t_vec = t_until_next_vac * np.ones(self.n_agents) + r_vec = next_vax_rate * np.ones(self.n_agents) + + # Normalized observations + obs_dict = dict() + obs_dict["a"] = {"t_until_next_vaccines": t_vec / self.delivery_interval} + obs_dict[self.world.planner.idx] = { + "t_until_next_vaccines": t_until_next_vac / self.delivery_interval + } + + if self.observe_rate: + obs_dict["a"]["next_vaccination_rate"] = r_vec + obs_dict["p"]["next_vaccination_rate"] = float(next_vax_rate) + + return obs_dict diff --git a/ai_economist/foundation/components/covid19_components_step.cu b/ai_economist/foundation/components/covid19_components_step.cu new file mode 100644 index 0000000..ba982ed --- /dev/null +++ b/ai_economist/foundation/components/covid19_components_step.cu @@ -0,0 +1,263 @@ +// Copyright (c) 2021, salesforce.com, inc. +// All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// For full license text, see the LICENSE file in the repo root +// or https://opensource.org/licenses/BSD-3-Clause + +extern "C" { + // CUDA version of the components in + // "ai_economist.foundation.components.covid19_components.py" + __global__ void CudaControlUSStateOpenCloseStatusStep( + int * stringency_level, + const int kActionCooldownPeriod, + int * action_in_cooldown_until, + const int * kDefaultAgentActionMask, + const int * kNoOpAgentActionMask, + const int kNumStringencyLevels, + int * actions, + float * obs_a_stringency_policy_indicators, + float * obs_a_action_mask, + float * obs_p_stringency_policy_indicators, + int * env_timestep_arr, + const int kNumAgents, + const int kEpisodeLength + ) { + const int kEnvId = blockIdx.x; + const int kAgentId = threadIdx.x; + + // Increment time ONCE -- only 1 thread can do this. + if (kAgentId == 0) { + env_timestep_arr[kEnvId] += 1; + } + + // Wait here until timestep has been updated + __syncthreads(); + + assert(env_timestep_arr[kEnvId] > 0 && + env_timestep_arr[kEnvId] <= kEpisodeLength); + assert (kAgentId <= kNumAgents - 1); + + // Update the stringency levels for the US states + if (kAgentId < (kNumAgents - 1)) { + // Indices for time-dependent and time-independent arrays + // Time dependent arrays have shapes + // (num_envs, kEpisodeLength + 1, kNumAgents - 1) + // Time independent arrays have shapes (num_envs, kNumAgents - 1) + const int kArrayIdxOffset = kEnvId * (kEpisodeLength + 1) * + (kNumAgents - 1); + int time_dependent_array_index_curr_t = kArrayIdxOffset + + env_timestep_arr[kEnvId] * (kNumAgents - 1) + kAgentId; + int time_dependent_array_index_prev_t = kArrayIdxOffset + + (env_timestep_arr[kEnvId] - 1) * (kNumAgents - 1) + kAgentId; + const int time_independent_array_index = kEnvId * (kNumAgents - 1) + + kAgentId; + + // action is not a NO-OP + if (actions[time_independent_array_index] != 0) { + stringency_level[time_dependent_array_index_curr_t] = + actions[time_independent_array_index]; + } else { + stringency_level[time_dependent_array_index_curr_t] = + stringency_level[time_dependent_array_index_prev_t]; + } + + if (env_timestep_arr[kEnvId] == action_in_cooldown_until[ + time_independent_array_index] + 1) { + if (actions[time_independent_array_index] != 0) { + assert(0 <= actions[time_independent_array_index] <= + kNumStringencyLevels); + action_in_cooldown_until[time_independent_array_index] += + kActionCooldownPeriod; + } else { + action_in_cooldown_until[time_independent_array_index] += 1; + } + } + + obs_a_stringency_policy_indicators[ + time_independent_array_index + ] = stringency_level[time_dependent_array_index_curr_t] / + static_cast(kNumStringencyLevels); + + // CUDA version of generate_masks() + for (int action_id = 0; action_id < (kNumStringencyLevels + 1); + action_id++) { + int action_mask_array_index = + kEnvId * (kNumStringencyLevels + 1) * + (kNumAgents - 1) + action_id * (kNumAgents - 1) + kAgentId; + if (env_timestep_arr[kEnvId] < action_in_cooldown_until[ + time_independent_array_index] + ) { + obs_a_action_mask[action_mask_array_index] = + kNoOpAgentActionMask[action_id]; + } else { + obs_a_action_mask[action_mask_array_index] = + kDefaultAgentActionMask[action_id]; + } + } + } + + // Update planner obs after all the agents' obs are updated + __syncthreads(); + + if (kAgentId == kNumAgents - 1) { + for (int ag_id = 0; ag_id < (kNumAgents - 1); ag_id++) { + const int kIndex = kEnvId * (kNumAgents - 1) + ag_id; + obs_p_stringency_policy_indicators[ + kIndex + ] = + obs_a_stringency_policy_indicators[ + kIndex + ]; + } + } + } + + __global__ void CudaFederalGovernmentSubsidyStep( + int * subsidy_level, + float * subsidy, + const int kSubsidyInterval, + const int kNumSubsidyLevels, + const float * KMaxDailySubsidyPerState, + const int * kDefaultPlannerActionMask, + const int * kNoOpPlannerActionMask, + int * actions, + float * obs_a_time_until_next_subsidy, + float * obs_a_current_subsidy_level, + float * obs_p_time_until_next_subsidy, + float * obs_p_current_subsidy_level, + float * obs_p_action_mask, + int * env_timestep_arr, + const int kNumAgents, + const int kEpisodeLength + ) { + const int kEnvId = blockIdx.x; + const int kAgentId = threadIdx.x; + + assert(env_timestep_arr[kEnvId] > 0 && + env_timestep_arr[kEnvId] <= kEpisodeLength); + assert (kAgentId <= kNumAgents - 1); + + int t_since_last_subsidy = env_timestep_arr[kEnvId] % + kSubsidyInterval; + + // Setting the (federal government) planner's subsidy level + // to be the subsidy level for all the US states + if (kAgentId < kNumAgents - 1) { + // Indices for time-dependent and time-independent arrays + // Time dependent arrays have shapes (num_envs, + // kEpisodeLength + 1, kNumAgents - 1) + // Time independent arrays have shapes (num_envs, kNumAgents - 1) + const int kArrayIdxOffset = kEnvId * (kEpisodeLength + 1) * + (kNumAgents - 1); + int time_dependent_array_index_curr_t = kArrayIdxOffset + + env_timestep_arr[kEnvId] * (kNumAgents - 1) + kAgentId; + int time_dependent_array_index_prev_t = kArrayIdxOffset + + (env_timestep_arr[kEnvId] - 1) * (kNumAgents - 1) + kAgentId; + const int time_independent_array_index = kEnvId * + (kNumAgents - 1) + kAgentId; + + if ((env_timestep_arr[kEnvId] - 1) % kSubsidyInterval == 0) { + assert(0 <= actions[kEnvId] <= kNumSubsidyLevels); + subsidy_level[time_dependent_array_index_curr_t] = + actions[kEnvId]; + } else { + subsidy_level[time_dependent_array_index_curr_t] = + subsidy_level[time_dependent_array_index_prev_t]; + } + // Setting the subsidies for the US states + // based on the federal government's subsidy level + subsidy[time_dependent_array_index_curr_t] = + subsidy_level[time_dependent_array_index_curr_t] * + KMaxDailySubsidyPerState[kAgentId] / kNumSubsidyLevels; + + obs_a_time_until_next_subsidy[ + time_independent_array_index] = + 1 - (t_since_last_subsidy / + static_cast(kSubsidyInterval)); + obs_a_current_subsidy_level[ + time_independent_array_index] = + subsidy_level[time_dependent_array_index_curr_t] / + static_cast(kNumSubsidyLevels); + } else if (kAgentId == (kNumAgents - 1)) { + for (int action_id = 0; action_id < kNumSubsidyLevels + 1; + action_id++) { + int action_mask_array_index = kEnvId * + (kNumSubsidyLevels + 1) + action_id; + if (env_timestep_arr[kEnvId] % kSubsidyInterval == 0) { + obs_p_action_mask[action_mask_array_index] = + kDefaultPlannerActionMask[action_id]; + } else { + obs_p_action_mask[action_mask_array_index] = + kNoOpPlannerActionMask[action_id]; + } + } + // Update planner obs after the agent's obs are updated + __syncthreads(); + + if (kAgentId == (kNumAgents - 1)) { + // Just use the values for agent id 0 + obs_p_time_until_next_subsidy[kEnvId] = + obs_a_time_until_next_subsidy[ + kEnvId * (kNumAgents - 1) + ]; + obs_p_current_subsidy_level[kEnvId] = + obs_a_current_subsidy_level[ + kEnvId * (kNumAgents - 1) + ]; + } + } + } + + __global__ void CudaVaccinationCampaignStep( + int * vaccinated, + const int * kNumVaccinesPerDelivery, + int * num_vaccines_available_t, + const int kDeliveryInterval, + const int kTimeWhenVaccineDeliveryBegins, + float * obs_a_vaccination_campaign_t_until_next_vaccines, + float * obs_p_vaccination_campaign_t_until_next_vaccines, + int * env_timestep_arr, + int kNumAgents, + int kEpisodeLength + ) { + const int kEnvId = blockIdx.x; + const int kAgentId = threadIdx.x; + + assert(env_timestep_arr[kEnvId] > 0 && env_timestep_arr[kEnvId] <= + kEpisodeLength); + assert(kTimeWhenVaccineDeliveryBegins > 0); + assert (kAgentId <= kNumAgents - 1); + + // CUDA version of generate observations() + int t_first_delivery = kTimeWhenVaccineDeliveryBegins + + kTimeWhenVaccineDeliveryBegins % kDeliveryInterval; + int next_t = env_timestep_arr[kEnvId] + 1; + float t_until_next_vac; + if (next_t <= t_first_delivery) { + t_until_next_vac = min( + 1, + (t_first_delivery - next_t) / kDeliveryInterval); + } else { + float t_since_last_vac = next_t % kDeliveryInterval; + t_until_next_vac = 1 - (t_since_last_vac / kDeliveryInterval); + } + + // Update the vaccinated numbers for just the US states + if (kAgentId < (kNumAgents - 1)) { + const int time_independent_array_index = kEnvId * + (kNumAgents - 1) + kAgentId; + if ((env_timestep_arr[kEnvId] >= kTimeWhenVaccineDeliveryBegins) && + (env_timestep_arr[kEnvId] % kDeliveryInterval == 0)) { + num_vaccines_available_t[time_independent_array_index] = + kNumVaccinesPerDelivery[kAgentId]; + } else { + num_vaccines_available_t[time_independent_array_index] = 0; + } + obs_a_vaccination_campaign_t_until_next_vaccines[ + time_independent_array_index] = t_until_next_vac; + } else if (kAgentId == kNumAgents - 1) { + obs_p_vaccination_campaign_t_until_next_vaccines[kEnvId] = + t_until_next_vac; + } + } +} diff --git a/ai_economist/foundation/components/move.py b/ai_economist/foundation/components/move.py new file mode 100644 index 0000000..89f9182 --- /dev/null +++ b/ai_economist/foundation/components/move.py @@ -0,0 +1,222 @@ +# Copyright (c) 2020, salesforce.com, inc. +# All rights reserved. +# SPDX-License-Identifier: BSD-3-Clause +# For full license text, see the LICENSE file in the repo root +# or https://opensource.org/licenses/BSD-3-Clause + +import numpy as np +from numpy.random import rand + +from ai_economist.foundation.base.base_component import ( + BaseComponent, + component_registry, +) + + +@component_registry.add +class Gather(BaseComponent): + """ + Allows mobile agents to move around the world and collect resources and prevents + agents from moving to invalid locations. + + Can be configured to include collection skill, where agents have heterogeneous + probabilities of collecting bonus resources without additional labor cost. + + Args: + move_labor (float): Labor cost associated with movement. Must be >= 0. + Default is 1.0. + collect_labor (float): Labor cost associated with collecting resources. This + cost is added (in addition to any movement cost) when the agent lands on + a tile that is populated with resources (triggering collection). + Must be >= 0. Default is 1.0. + skill_dist (str): Distribution type for sampling skills. Default ("none") + gives all agents identical skill equal to a bonus prob of 0. "pareto" and + "lognormal" sample skills from the associated distributions. + """ + + name = "Gather" + required_entities = ["Coin", "House", "Labor"] + agent_subclasses = ["BasicMobileAgent"] + + def __init__( + self, + *base_component_args, + move_labor=1.0, + collect_labor=1.0, + skill_dist="none", + **base_component_kwargs + ): + super().__init__(*base_component_args, **base_component_kwargs) + + self.move_labor = float(move_labor) + assert self.move_labor >= 0 + + self.collect_labor = float(collect_labor) + assert self.collect_labor >= 0 + + self.skill_dist = skill_dist.lower() + assert self.skill_dist in ["none", "pareto", "lognormal"] + + self.gathers = [] + + self._aidx = np.arange(self.n_agents)[:, None].repeat(4, axis=1) + self._roff = np.array([[0, 0, -1, 1]]) + self._coff = np.array([[-1, 1, 0, 0]]) + + # Required methods for implementing components + # -------------------------------------------- + + def get_n_actions(self, agent_cls_name): + """ + See base_component.py for detailed description. + + Adds 4 actions (move up, down, left, or right) for mobile agents. + """ + # This component adds 4 action that agents can take: + # move up, down, left, or right + if agent_cls_name == "BasicMobileAgent": + return 4 + return None + + def get_additional_state_fields(self, agent_cls_name): + """ + See base_component.py for detailed description. + + For mobile agents, add state field for collection skill. + """ + if agent_cls_name not in self.agent_subclasses: + return {} + if agent_cls_name == "BasicMobileAgent": + return {"bonus_gather_prob": 0.0} + raise NotImplementedError + + def component_step(self): + """ + See base_component.py for detailed description. + + Move to adjacent, unoccupied locations. Collect resources when moving to + populated resource tiles, adding the resource to the agent's inventory and + de-populating it from the tile. + """ + world = self.world + + gathers = [] + for agent in world.get_random_order_agents(): + + if self.name not in agent.action: + return + action = agent.get_component_action(self.name) + + r, c = [int(x) for x in agent.loc] + + if action == 0: # NO-OP! + new_r, new_c = r, c + + elif action <= 4: + if action == 1: # Left + new_r, new_c = r, c - 1 + elif action == 2: # Right + new_r, new_c = r, c + 1 + elif action == 3: # Up + new_r, new_c = r - 1, c + else: # action == 4, # Down + new_r, new_c = r + 1, c + + # Attempt to move the agent (if the new coordinates aren't accessible, + # nothing will happen) + new_r, new_c = world.set_agent_loc(agent, new_r, new_c) + + # If the agent did move, incur the labor cost of moving + if (new_r != r) or (new_c != c): + agent.state["endogenous"]["Labor"] += self.move_labor + + else: + raise ValueError + + for resource, health in world.location_resources(new_r, new_c).items(): + if health >= 1: + n_gathered = 1 + (rand() < agent.state["bonus_gather_prob"]) + agent.state["inventory"][resource] += n_gathered + world.consume_resource(resource, new_r, new_c) + # Incur the labor cost of collecting a resource + agent.state["endogenous"]["Labor"] += self.collect_labor + # Log the gather + gathers.append( + dict( + agent=agent.idx, + resource=resource, + n=n_gathered, + loc=[new_r, new_c], + ) + ) + + self.gathers.append(gathers) + + def generate_observations(self): + """ + See base_component.py for detailed description. + + Here, agents observe their collection skill. The planner does not observe + anything from this component. + """ + return { + str(agent.idx): {"bonus_gather_prob": agent.state["bonus_gather_prob"]} + for agent in self.world.agents + } + + def generate_masks(self, completions=0): + """ + See base_component.py for detailed description. + + Prevent moving to adjacent tiles that are already occupied (or outside the + boundaries of the world) + """ + world = self.world + + coords = np.array([agent.loc for agent in world.agents])[:, :, None] + ris = coords[:, 0] + self._roff + 1 + cis = coords[:, 1] + self._coff + 1 + + occ = np.pad(world.maps.unoccupied, ((1, 1), (1, 1))) + acc = np.pad(world.maps.accessibility, ((0, 0), (1, 1), (1, 1))) + mask_array = np.logical_and(occ[ris, cis], acc[self._aidx, ris, cis]).astype( + np.float32 + ) + + masks = {agent.idx: mask_array[i] for i, agent in enumerate(world.agents)} + + return masks + + # For non-required customization + # ------------------------------ + + def additional_reset_steps(self): + """ + See base_component.py for detailed description. + + Re-sample agents' collection skills. + """ + for agent in self.world.agents: + if self.skill_dist == "none": + bonus_rate = 0.0 + elif self.skill_dist == "pareto": + bonus_rate = np.minimum(2, np.random.pareto(3)) / 2 + elif self.skill_dist == "lognormal": + bonus_rate = np.minimum(2, np.random.lognormal(-2.022, 0.938)) / 2 + else: + raise NotImplementedError + agent.state["bonus_gather_prob"] = float(bonus_rate) + + self.gathers = [] + + def get_dense_log(self): + """ + Log resource collections. + + Returns: + gathers (list): A list of gather events. Each entry corresponds to a single + timestep and contains a description of any resource gathers that + occurred on that timestep. + + """ + return self.gathers diff --git a/ai_economist/foundation/components/redistribution.py b/ai_economist/foundation/components/redistribution.py new file mode 100644 index 0000000..d2177ab --- /dev/null +++ b/ai_economist/foundation/components/redistribution.py @@ -0,0 +1,1202 @@ +# Copyright (c) 2020, salesforce.com, inc. +# All rights reserved. +# SPDX-License-Identifier: BSD-3-Clause +# For full license text, see the LICENSE file in the repo root +# or https://opensource.org/licenses/BSD-3-Clause + +from copy import deepcopy + +import numpy as np + +from ai_economist.foundation.base.base_component import ( + BaseComponent, + component_registry, +) +from ai_economist.foundation.components.utils import ( + annealed_tax_limit, + annealed_tax_mask, +) + + +@component_registry.add +class WealthRedistribution(BaseComponent): + """Redistributes the total coin of the mobile agents as evenly as possible. + + Note: + If this component is used, it should always be the last component in the order! + """ + + name = "WealthRedistribution" + required_entities = ["Coin"] + agent_subclasses = ["BasicMobileAgent"] + + """ + Required methods for implementing components + -------------------------------------------- + """ + + def get_n_actions(self, agent_cls_name): + """This component is passive: it does not add any actions.""" + return + + def get_additional_state_fields(self, agent_cls_name): + """This component does not add any state fields.""" + return {} + + def component_step(self): + """ + See base_component.py for detailed description. + + Redistributes inventory coins so that all agents have equal coin endowment. + """ + world = self.world + + # Divide coins evenly + ic = np.array([agent.state["inventory"]["Coin"] for agent in world.agents]) + ec = np.array([agent.state["escrow"]["Coin"] for agent in world.agents]) + tc = np.sum(ic + ec) + target_share = tc / self.n_agents + for agent in world.agents: + agent.state["inventory"]["Coin"] = float(target_share - ec[agent.idx]) + + ic = np.array([agent.state["inventory"]["Coin"] for agent in world.agents]) + ec = np.array([agent.state["escrow"]["Coin"] for agent in world.agents]) + tc_next = np.sum(ic + ec) + assert np.abs(tc - tc_next) < 1 + + def generate_observations(self): + """This component does not add any observations.""" + obs = {} + return obs + + def generate_masks(self, completions=0): + """Passive component. Masks are empty.""" + masks = {} + return masks + + +@component_registry.add +class PeriodicBracketTax(BaseComponent): + """Periodically collect income taxes from agents and do lump-sum redistribution. + + Note: + If this component is used, it should always be the last component in the order! + + Args: + disable_taxes (bool): Whether to disable any tax collection, effectively + enforcing that tax rates are always 0. Useful for removing taxes without + changing the observation space. Default is False (taxes enabled). + tax_model (str): Which tax model to use for setting taxes. + "model_wrapper" (default) uses the actions of the planner agent; + "saez" uses an adaptation of the theoretical optimal taxation formula + derived in https://www.nber.org/papers/w7628. + "us-federal-single-filer-2018-scaled" uses US federal tax rates from 2018; + "fixed-bracket-rates" uses the rates supplied in fixed_bracket_rates. + period (int): Length of a tax period in environment timesteps. Taxes are + updated at the start of each period and collected/redistributed at the + end of each period. Must be > 0. Default is 100 timesteps. + rate_min (float): Minimum tax rate within a bracket. Must be >= 0 (default). + rate_max (float): Maximum tax rate within a bracket. Must be <= 1 (default). + rate_disc (float): (Only applies for "model_wrapper") the interval separating + discrete tax rates that the planner can select. Default of 0.05 means, + for example, the planner can select among [0.0, 0.05, 0.10, ... 1.0]. + Must be > 0 and < 1. + n_brackets (int): How many tax brackets to use. Must be >=2. Default is 5. + top_bracket_cutoff (float): The income at the left end of the last tax + bracket. Must be >= 10. Default is 100 coin. + usd_scaling (float): Scale by which to divide the US Federal bracket cutoffs + when using bracket_spacing = "us-federal". Must be > 0. Default is 1000. + bracket_spacing (str): How bracket cutoffs should be spaced. + "us-federal" (default) uses scaled cutoffs from the 2018 US Federal + taxes, with scaling set by usd_scaling (ignores n_brackets and + top_bracket_cutoff); + "linear" linearly spaces the n_bracket cutoffs between 0 and + top_bracket_cutoff; + "log" is similar to "linear" but with logarithmic spacing. + fixed_bracket_rates (list): Required if tax_model=="fixed-bracket-rates". A + list of fixed marginal rates to use for each bracket. Length must be + equal to the number of brackets (7 for "us-federal" spacing, n_brackets + otherwise). + pareto_weight_type (str): Type of Pareto weights to use when computing tax + rates using the Saez formula. "inverse_income" (default) uses 1/z; + "uniform" uses 1. + saez_fixed_elas (float, optional): If supplied, this value will be used as + the elasticity estimate when computing tax rates using the Saez formula. + If not given (default), elasticity will be estimated empirically. + tax_annealing_schedule (list, optional): A length-2 list of + [tax_annealing_warmup, tax_annealing_slope] describing the tax annealing + schedule. See annealed_tax_mask function for details. Default behavior is + no tax annealing. + """ + + name = "PeriodicBracketTax" + component_type = "PeriodicTax" + required_entities = ["Coin"] + agent_subclasses = ["BasicMobileAgent", "BasicPlanner"] + + def __init__( + self, + *base_component_args, + disable_taxes=False, + tax_model="model_wrapper", + period=100, + rate_min=0.0, + rate_max=1.0, + rate_disc=0.05, + n_brackets=5, + top_bracket_cutoff=100, + usd_scaling=1000.0, + bracket_spacing="us-federal", + fixed_bracket_rates=None, + pareto_weight_type="inverse_income", + saez_fixed_elas=None, + tax_annealing_schedule=None, + **base_component_kwargs + ): + super().__init__(*base_component_args, **base_component_kwargs) + + # Whether to turn off taxes. Disabling taxes will prevent any taxes from + # being collected but the observation space will be the same as if taxes were + # enabled, which can be useful for controlled tax/no-tax comparisons. + self.disable_taxes = bool(disable_taxes) + + # How to set taxes. + self.tax_model = tax_model + assert self.tax_model in [ + "model_wrapper", + "us-federal-single-filer-2018-scaled", + "saez", + "fixed-bracket-rates", + ] + + # How many timesteps a tax period lasts. + self.period = int(period) + assert self.period > 0 + + # Minimum marginal bracket rate + self.rate_min = 0.0 if self.disable_taxes else float(rate_min) + # Maximum marginal bracket rate + self.rate_max = 0.0 if self.disable_taxes else float(rate_max) + assert 0 <= self.rate_min <= self.rate_max <= 1.0 + + # Interval for discretizing tax rate options + # (only applies if tax_model == "model_wrapper"). + self.rate_disc = float(rate_disc) + + self.use_discretized_rates = self.tax_model == "model_wrapper" + + if self.use_discretized_rates: + self.disc_rates = np.arange( + self.rate_min, self.rate_max + self.rate_disc, self.rate_disc + ) + self.disc_rates = self.disc_rates[self.disc_rates <= self.rate_max] + assert len(self.disc_rates) > 1 or self.disable_taxes + self.n_disc_rates = len(self.disc_rates) + else: + self.disc_rates = None + self.n_disc_rates = 0 + + # === income bracket definitions === + self.n_brackets = int(n_brackets) + assert self.n_brackets >= 2 + + self.top_bracket_cutoff = float(top_bracket_cutoff) + assert self.top_bracket_cutoff >= 10 + + self.usd_scale = float(usd_scaling) + assert self.usd_scale > 0 + + self.bracket_spacing = bracket_spacing.lower() + assert self.bracket_spacing in ["linear", "log", "us-federal"] + + if self.bracket_spacing == "linear": + self.bracket_cutoffs = np.linspace( + 0, self.top_bracket_cutoff, self.n_brackets + ) + + elif self.bracket_spacing == "log": + b0_max = self.top_bracket_cutoff / (2 ** (self.n_brackets - 2)) + self.bracket_cutoffs = np.concatenate( + [ + [0], + 2 + ** np.linspace( + np.log2(b0_max), + np.log2(self.top_bracket_cutoff), + n_brackets - 1, + ), + ] + ) + elif self.bracket_spacing == "us-federal": + self.bracket_cutoffs = ( + np.array([0, 9700, 39475, 84200, 160725, 204100, 510300]) + / self.usd_scale + ) + self.n_brackets = len(self.bracket_cutoffs) + self.top_bracket_cutoff = float(self.bracket_cutoffs[-1]) + else: + raise NotImplementedError + + self.bracket_edges = np.concatenate([self.bracket_cutoffs, [np.inf]]) + self.bracket_sizes = self.bracket_edges[1:] - self.bracket_edges[:-1] + + assert self.bracket_cutoffs[0] == 0 + + if self.tax_model == "us-federal-single-filer-2018-scaled": + assert self.bracket_spacing == "us-federal" + + if self.tax_model == "fixed-bracket-rates": + assert isinstance(fixed_bracket_rates, (tuple, list)) + assert np.min(fixed_bracket_rates) >= 0 + assert np.max(fixed_bracket_rates) <= 1 + assert len(fixed_bracket_rates) == self.n_brackets + self._fixed_bracket_rates = np.array(fixed_bracket_rates) + else: + self._fixed_bracket_rates = None + + # === bracket tax rates === + self.curr_bracket_tax_rates = np.zeros_like(self.bracket_cutoffs) + self.curr_rate_indices = [0 for _ in range(self.n_brackets)] + + # === Pareto weights, elasticity === + self.pareto_weight_type = pareto_weight_type + self.elas_tm1 = 0.5 + self.elas_t = 0.5 + self.log_z0_tm1 = 0 + self.log_z0_t = 0 + + self._saez_fixed_elas = saez_fixed_elas + if self._saez_fixed_elas is not None: + self._saez_fixed_elas = float(self._saez_fixed_elas) + assert self._saez_fixed_elas >= 0 + + # Size of the local buffer. In a distributed context, the global buffer size + # will be capped at n_replicas * _buffer_size. + # NOTE: Saez will use random taxes until it has self._buffer_size samples. + self._buffer_size = 500 + self._reached_min_samples = False + self._additions_this_episode = 0 + # Local buffer maintained by this replica. + self._local_saez_buffer = [] + # "Global" buffer obtained by combining local buffers of individual replicas. + self._global_saez_buffer = [] + + self._saez_n_estimation_bins = 100 + self._saez_top_rate_cutoff = self.bracket_cutoffs[-1] + self._saez_income_bin_edges = np.linspace( + 0, self._saez_top_rate_cutoff, self._saez_n_estimation_bins + 1 + ) + self._saez_income_bin_sizes = np.concatenate( + [ + self._saez_income_bin_edges[1:] - self._saez_income_bin_edges[:-1], + [np.inf], + ] + ) + self.running_avg_tax_rates = np.zeros_like(self.curr_bracket_tax_rates) + + # === tax cycle definitions === + self.tax_cycle_pos = 1 + self.last_coin = [0 for _ in range(self.n_agents)] + self.last_income = [0 for _ in range(self.n_agents)] + self.last_marginal_rate = [0 for _ in range(self.n_agents)] + self.last_effective_tax_rate = [0 for _ in range(self.n_agents)] + + # === trackers === + self.total_collected_taxes = 0 + self.all_effective_tax_rates = [] + self._schedules = {"{:03d}".format(int(r)): [0] for r in self.bracket_cutoffs} + self._occupancy = {"{:03d}".format(int(r)): 0 for r in self.bracket_cutoffs} + self.taxes = [] + + # === tax annealing === + # for annealing of non-planner max taxes. + self._annealed_rate_max = float(self.rate_max) + self._last_completions = 0 + + # for annealing of planner actions. + self.tax_annealing_schedule = tax_annealing_schedule + if tax_annealing_schedule is not None: + assert isinstance(self.tax_annealing_schedule, (tuple, list)) + self._annealing_warmup = self.tax_annealing_schedule[0] + self._annealing_slope = self.tax_annealing_schedule[1] + self._annealed_rate_max = annealed_tax_limit( + self._last_completions, + self._annealing_warmup, + self._annealing_slope, + self.rate_max, + ) + else: + self._annealing_warmup = None + self._annealing_slope = None + + if self.tax_model == "model_wrapper" and not self.disable_taxes: + planner_action_tuples = self.get_n_actions("BasicPlanner") + self._planner_tax_val_dict = { + k: self.disc_rates for k, v in planner_action_tuples + } + else: + self._planner_tax_val_dict = {} + self._planner_masks = None + + # === placeholders === + self._curr_rates_obs = np.array(self.curr_marginal_rates) + self._last_income_obs = np.array(self.last_income) / self.period + self._last_income_obs_sorted = self._last_income_obs[ + np.argsort(self._last_income_obs) + ] + + # Methods for getting/setting marginal tax rates + # ---------------------------------------------- + + # ------- US Federal taxes + @property + def us_federal_single_filer_2018_scaled(self): + """ + https://turbotax.intuit.com/tax-tips/irs-tax-return/current-federal-tax-rate-schedules/L7Bjs1EAD + If taxable income is over— + but not over— + the tax is: + $0 + $9,700 + 10% of the amount over $0 + $9,700 + $39,475 + $970 plus 12% of the amount over $9,700 + $39,475 + $84,200 + $4,543 plus 22% of the amount over $39,475 + $84,200 + $160,725 + $14,382 plus 24% of the amount over $84,200 + $160,725 + $204,100 + $32,748 plus 32% of the amount over $160,725 + $204,100 + $510,300 + $46,628 plus 35% of the amount over $204,100 + $510,300 + no limit + $153,798 plus 37% of the amount over $510,300 + """ + return [0.1, 0.12, 0.22, 0.24, 0.32, 0.35, 0.37] + + # ------- fixed-bracket-rates + @property + def fixed_bracket_rates(self): + """Return whatever fixed bracket rates were set during initialization.""" + return self._fixed_bracket_rates + + @property + def curr_rate_max(self): + """Maximum allowable tax rate, given current progress of any tax annealing.""" + if self.tax_annealing_schedule is None: + return self.rate_max + return self._annealed_rate_max + + @property + def curr_marginal_rates(self): + """The current set of marginal tax bracket rates.""" + if self.use_discretized_rates: + return self.disc_rates[self.curr_rate_indices] + + if self.tax_model == "us-federal-single-filer-2018-scaled": + marginal_tax_bracket_rates = np.minimum( + np.array(self.us_federal_single_filer_2018_scaled), self.curr_rate_max + ) + elif self.tax_model == "saez": + marginal_tax_bracket_rates = np.minimum( + self.curr_bracket_tax_rates, self.curr_rate_max + ) + elif self.tax_model == "fixed-bracket-rates": + marginal_tax_bracket_rates = np.minimum( + np.array(self.fixed_bracket_rates), self.curr_rate_max + ) + else: + raise NotImplementedError + + return marginal_tax_bracket_rates + + def set_new_period_rates_model(self): + """Update taxes using actions from the tax model.""" + if self.disable_taxes: + return + + # AI version + for i, bracket in enumerate(self.bracket_cutoffs): + planner_action = self.world.planner.get_component_action( + self.name, "TaxIndexBracket_{:03d}".format(int(bracket)) + ) + if planner_action == 0: + pass + elif planner_action <= self.n_disc_rates: + self.curr_rate_indices[i] = int(planner_action - 1) + else: + raise ValueError + + # ------- Saez formula + def compute_and_set_new_period_rates_from_saez_formula( + self, update_elas_tm1=True, update_log_z0_tm1=True + ): + """Estimates/sets optimal rates using adaptation of Saez formula + + See: https://www.nber.org/papers/w7628 + """ + # Until we reach the min sample number, keep checking if we have reached it. + if not self._reached_min_samples: + # Note: self.saez_buffer includes the global buffer (if applicable). + if len(self.saez_buffer) >= self._buffer_size: + self._reached_min_samples = True + + # If no enough samples, use random taxes. + if not self._reached_min_samples: + self.curr_bracket_tax_rates = np.random.uniform( + low=self.rate_min, + high=self.curr_rate_max, + size=self.curr_bracket_tax_rates.shape, + ) + return + + incomes_and_marginal_rates = np.array(self.saez_buffer) + + # Elasticity assumed constant for all incomes. + # (Run this for the sake of tracking the estimate; will not actually use the + # estimate if using fixed elasticity). + if update_elas_tm1: + self.elas_tm1 = float(self.elas_t) + if update_log_z0_tm1: + self.log_z0_tm1 = float(self.log_z0_t) + + elas_t, log_z0_t = self.estimate_uniform_income_elasticity( + incomes_and_marginal_rates, + elas_df=0.98, + elas_tm1=self.elas_tm1, + log_z0_tm1=self.log_z0_tm1, + verbose=False, + ) + + if update_elas_tm1: + self.elas_t = float(elas_t) + if update_log_z0_tm1: + self.log_z0_t = float(log_z0_t) + + # If a fixed estimate has been specified, use it in the formulas below. + if self._saez_fixed_elas is not None: + elas_t = float(self._saez_fixed_elas) + + # Get Saez parameters at each income bin + # to compute a marginal tax rate schedule. + binned_gzs, binned_azs = self.get_binned_saez_welfare_weight_and_pareto_params( + population_incomes=incomes_and_marginal_rates[:, 0] + ) + + # Use the elasticity to compute this binned schedule using the Saez formula. + binned_marginal_tax_rates = self.get_saez_marginal_rates( + binned_gzs, binned_azs, elas_t + ) + + # Adapt the saez tax schedule to the tax brackets. + self.curr_bracket_tax_rates = np.clip( + self.bracketize_schedule( + bin_marginal_rates=binned_marginal_tax_rates, + bin_edges=self._saez_income_bin_edges, + bin_sizes=self._saez_income_bin_sizes, + ), + self.rate_min, + self.curr_rate_max, + ) + + self.running_avg_tax_rates = (self.running_avg_tax_rates * 0.99) + ( + self.curr_bracket_tax_rates * 0.01 + ) + + # Implementation of the Saez formula in this periodic, bracketed setting + # ---------------------------------------------------------------------- + @property + def saez_buffer(self): + if not self._global_saez_buffer: + saez_buffer = self._local_saez_buffer + elif self._additions_this_episode == 0: + saez_buffer = self._global_saez_buffer + else: + saez_buffer = ( + self._global_saez_buffer + + self._local_saez_buffer[-self._additions_this_episode :] + ) + return saez_buffer + + def get_local_saez_buffer(self): + return self._local_saez_buffer + + def set_global_saez_buffer(self, global_saez_buffer): + assert isinstance(global_saez_buffer, list) + assert len(global_saez_buffer) >= len(self._local_saez_buffer) + self._global_saez_buffer = global_saez_buffer + + def _update_saez_buffer(self, tax_info_t): + # Update the buffer. + for a_idx in range(self.n_agents): + z_t = tax_info_t[str(a_idx)]["income"] + tau_t = tax_info_t[str(a_idx)]["marginal_rate"] + self._local_saez_buffer.append([z_t, tau_t]) + self._additions_this_episode += 1 + + while len(self._local_saez_buffer) > self._buffer_size: + _ = self._local_saez_buffer.pop(0) + + def reset_saez_buffers(self): + self._local_saez_buffer = [] + self._global_saez_buffer = [] + self._additions_this_episode = 0 + self._reached_min_samples = False + + def estimate_uniform_income_elasticity( + self, + observed_incomes_and_marginal_rates, + elas_df=0.98, + elas_tm1=0.5, + log_z0_tm1=0.5, + verbose=False, + ): + """Estimate elasticity using Ordinary Least Squares regression. + OLS: https://en.wikipedia.org/wiki/Ordinary_least_squares + Estimating elasticity: https://www.nber.org/papers/w7512 + """ + zs = [] + taus = [] + + for z_t, tau_t in observed_incomes_and_marginal_rates: + # If z_t is <=0 or tau_t is >=1, the operations below will give us nans + if z_t > 0 and tau_t < 1: + zs.append(z_t) + taus.append(tau_t) + + if len(zs) < 10: + return float(elas_tm1), float(log_z0_tm1) + if np.std(taus) < 1e-6: + return float(elas_tm1), float(log_z0_tm1) + + # Regressing log income against log 1-marginal_rate. + x = np.log(np.maximum(1 - np.array(taus), 1e-9)) + # (bias term) + b = np.ones_like(x) + # Perform OLS. + X = np.stack([x, b]).T # Stack linear & bias terms + Y = np.log(np.maximum(np.array(zs), 1e-9)) # Regression targets + XXi = np.linalg.inv(X.T.dot(X)) + XY = X.T.dot(Y) + elas, log_z0 = XXi.T.dot(XY) + + warn_less_than_0 = elas < 0 + instant_elas_t = np.maximum(elas, 0.0) + + elas_t = ((1 - elas_df) * instant_elas_t) + (elas_df * elas_tm1) + + if verbose: + if warn_less_than_0: + print("\nWARNING: Recent elasticity estimate is < 0.") + print("Running elasticity estimate: {:.2f}\n".format(elas_t)) + else: + print("\nRunning elasticity estimate: {:.2f}\n".format(elas_t)) + + return elas_t, log_z0 + + def get_binned_saez_welfare_weight_and_pareto_params(self, population_incomes): + def clip(x, lo=None, hi=None): + if lo is not None: + x = max(lo, x) + if hi is not None: + x = min(x, hi) + return x + + def bin_z(left, right): + return 0.5 * (left + right) + + def get_cumul(counts, incomes_below, incomes_above): + n_below = len(incomes_below) + n_above = len(incomes_above) + n_total = np.sum(counts) + n_below + n_above + + def p(i, counts): + return counts[i] / n_total + + # Probability that an income is below the taxable threshold. + p_below = n_below / n_total + + # pz = p(z' = z): probability that [binned] income z' occurs in bin z. + pz = [p(i, counts) for i in range(len(counts))] + [n_above / n_total] + + # Pz = p(z' <= z): Probability z' is less-than or equal to z. + cum_pz = [pz[0] + p_below] + for p in pz[1:]: + cum_pz.append(clip(cum_pz[-1] + p, 0, 1.0)) + + return np.array(pz), np.array(cum_pz) + + def compute_binned_g_distribution(counts, lefts, incomes): + def pareto(z): + if self.pareto_weight_type == "uniform": + pareto_weights = np.ones_like(z) + elif self.pareto_weight_type == "inverse_income": + pareto_weights = 1.0 / np.maximum(1, z) + else: + raise NotImplementedError + return pareto_weights + + incomes_below = incomes[incomes < lefts[0]] + incomes_above = incomes[incomes > lefts[-1]] + + # The total (unnormalized) Pareto weight of untaxable incomes. + if len(incomes_below) > 0: + pareto_weight_below = np.sum(pareto(np.maximum(incomes_below, 0))) + else: + pareto_weight_below = 0 + + # The total (unnormalized) Pareto weight within each bin. + if len(incomes_above) > 0: + pareto_weight_above = np.sum(pareto(incomes_above)) + else: + pareto_weight_above = 0 + + # The total (unnormalized) Pareto weight within each bin. + pareto_weight_per_bin = counts * pareto(bin_z(lefts[:-1], lefts[1:])) + + # The aggregate (unnormalized) Pareto weight of all incomes. + cumulative_pareto_weights = pareto_weight_per_bin.sum() + cumulative_pareto_weights += pareto_weight_below + cumulative_pareto_weights += pareto_weight_above + + # Normalize so that the Pareto density sums to 1. + pareto_norm = cumulative_pareto_weights + 1e-9 + unnormalized_pareto_density = np.concatenate( + [pareto_weight_per_bin, [pareto_weight_above]] + ) + normalized_pareto_density = unnormalized_pareto_density / pareto_norm + + # Aggregate Pareto weight of earners with income greater-than or equal to z. + cumulative_pareto_density_geq_z = np.cumsum( + normalized_pareto_density[::-1] + )[::-1] + + # Probability that [binned] income z' is greather-than or equal to z. + pz, _ = get_cumul(counts, incomes_below, incomes_above) + cumulative_prob_geq_z = np.cumsum(pz[::-1])[::-1] + + # Average (normalized) Pareto weight of earners with income >= z. + geq_z_norm = cumulative_prob_geq_z + 1e-9 + avg_pareto_weight_geq_z = cumulative_pareto_density_geq_z / geq_z_norm + + def interpolate_gzs(gz): + # Assume incomes within a bin are evenly distributed within that bin + # and re-compute accordingly. + gz_at_left_edge = gz[:-1] + gz_at_right_edge = gz[1:] + + avg_bin_gz = 0.5 * (gz_at_left_edge + gz_at_right_edge) + # Re-attach the gz of the top tax rate (does not need to be + # interpolated). + gzs = np.concatenate([avg_bin_gz, [gz[-1]]]) + return gzs + + return interpolate_gzs(avg_pareto_weight_geq_z) + + def compute_binned_a_distribution(counts, lefts, incomes): + incomes_below = incomes[incomes < lefts[0]] + incomes_above = incomes[incomes > lefts[-1]] + + # z is defined as the MIDDLE point in a bin. + # So for a bin [left, right] -> z = (left + right) / 2. + Az = [] + + # cum_pz = p(z' <= z): Probability z' is less-than or equal to z + pz, cum_pz = get_cumul(counts, incomes_below, incomes_above) + + # Probability z' is greater-than or equal to z + # Note: The "0.5" coefficient gives results more consistent with theory; it + # accounts for the assumption that incomes within a particular bin are + # uniformly spread between the left & right edges of that bin. + p_geq_z = 1 - cum_pz + (0.5 * pz) + + T = len(lefts[:-1]) + + for i in range(T): + if pz[i] == 0: + Az.append(np.nan) + else: + z = bin_z(lefts[i], lefts[i + 1]) + # paz = z * pz[i] / (clip(1 - Pz[i], 0, 1) + 1e-9) + paz = z * pz[i] / (clip(p_geq_z[i], 0, 1) + 1e-9) # defn of A(z) + paz = paz / (lefts[i + 1] - lefts[i]) # norm by bin width + Az.append(paz) + + # Az for the incomes past the top cutoff, + # the bin is [left, infinity]: there is no "middle". + # Hence, use the mean value in the last bin. + if len(incomes_above) > 0: + cutoff = lefts[-1] + avg_income_above_cutoff = np.mean(incomes_above) + # use a special formula to compute A(z) + Az_above = avg_income_above_cutoff / ( + avg_income_above_cutoff - cutoff + 1e-9 + ) + else: + Az_above = 0.0 + + return np.concatenate([Az, [Az_above]]) + + counts, lefts = np.histogram( + population_incomes, bins=self._saez_income_bin_edges + ) + population_gz = compute_binned_g_distribution(counts, lefts, population_incomes) + population_az = compute_binned_a_distribution(counts, lefts, population_incomes) + + # Return the binned stats used to create a schedule of marginal rates. + return population_gz, population_az + + @staticmethod + def get_saez_marginal_rates(binned_gz, binned_az, elas, interpolate=True): + # Marginal rates within each income bin (last tau is the top tax rate). + taus = (1.0 - binned_gz) / (1.0 - binned_gz + binned_az * elas + 1e-9) + + if interpolate: + # In bins where there were no incomes found, tau is nan. + # Interpolate to fill the gaps. + last_real_rate = 0.0 + last_real_tidx = -1 + for i, tau in enumerate(taus): + # The current tax rate is a real number. + if not np.isnan(tau): + # This is the end of a gap. Interpolate. + if (i - last_real_tidx) > 1: + assert ( + i != 0 + ) # This should never trigger for the first tax bin. + gap_indices = list(range(last_real_tidx + 1, i)) + intermediate_rates = np.linspace( + last_real_rate, tau, len(gap_indices) + 2 + )[1:-1] + assert len(gap_indices) == len(intermediate_rates) + for gap_index, intermediate_rate in zip( + gap_indices, intermediate_rates + ): + taus[gap_index] = intermediate_rate + # Update the tracker. + last_real_rate = float(tau) + last_real_tidx = int(i) + + # The current tax rate is a nan. Continue without updating + # the tracker (indicating the presence of a gap). + else: + pass + + return taus + + def bracketize_schedule(self, bin_marginal_rates, bin_edges, bin_sizes): + # Compute the amount of tax each bracket would collect + # if income was >= the right edge. + # Divide by the bracket size to get + # the average marginal rate within that bracket. + last_bracket_total = 0 + bracket_avg_marginal_rates = [] + for b_idx, income in enumerate(self.bracket_cutoffs[1:]): + # How much income occurs within each bin + # (including the open-ended, top "bin"). + past_cutoff = np.maximum(0, income - bin_edges) + bin_income = np.minimum(bin_sizes, past_cutoff) + + # To get the total taxes due, + # multiply the income within each bin by that bin's marginal rate. + bin_taxes = bin_marginal_rates * bin_income + taxes_due = np.maximum(0, np.sum(bin_taxes)) + + bracket_tax_burden = taxes_due - last_bracket_total + bracket_size = self.bracket_sizes[b_idx] + + bracket_avg_marginal_rates.append(bracket_tax_burden / bracket_size) + last_bracket_total = taxes_due + + # The top bracket tax rate is computed directly already. + bracket_avg_marginal_rates.append(bin_marginal_rates[-1]) + + bracket_rates = np.array(bracket_avg_marginal_rates) + assert len(bracket_rates) == self.n_brackets + + return bracket_rates + + # Methods for collecting and redistributing taxes + # ----------------------------------------------- + + def income_bin(self, income): + """Return index of tax bin in which income falls.""" + if income < 0: + return 0.0 + meets_min = income >= self.bracket_edges[:-1] + under_max = income < self.bracket_edges[1:] + bracket_bool = meets_min * under_max + return self.bracket_cutoffs[np.argmax(bracket_bool)] + + def marginal_rate(self, income): + """Return the marginal tax rate applied at this income level.""" + if income < 0: + return 0.0 + meets_min = income >= self.bracket_edges[:-1] + under_max = income < self.bracket_edges[1:] + bracket_bool = meets_min * under_max + return self.curr_marginal_rates[np.argmax(bracket_bool)] + + def taxes_due(self, income): + """Return the total amount of taxes due at this income level.""" + past_cutoff = np.maximum(0, income - self.bracket_cutoffs) + bin_income = np.minimum(self.bracket_sizes, past_cutoff) + bin_taxes = self.curr_marginal_rates * bin_income + return np.sum(bin_taxes) + + def enact_taxes(self): + """Calculate period income & tax burden. Collect taxes and redistribute.""" + net_tax_revenue = 0 + tax_dict = dict( + schedule=np.array(self.curr_marginal_rates), + cutoffs=np.array(self.bracket_cutoffs), + ) + + for curr_rate, bracket_cutoff in zip( + self.curr_marginal_rates, self.bracket_cutoffs + ): + self._schedules["{:03d}".format(int(bracket_cutoff))].append( + float(curr_rate) + ) + + self.last_income = [] + self.last_effective_tax_rate = [] + self.last_marginal_rate = [] + for agent, last_coin in zip(self.world.agents, self.last_coin): + income = agent.total_endowment("Coin") - last_coin + tax_due = self.taxes_due(income) + effective_taxes = np.minimum( + agent.state["inventory"]["Coin"], tax_due + ) # Don't take from escrow. + marginal_rate = self.marginal_rate(income) + effective_tax_rate = float(effective_taxes / np.maximum(0.000001, income)) + tax_dict[str(agent.idx)] = dict( + income=float(income), + tax_paid=float(effective_taxes), + marginal_rate=marginal_rate, + effective_rate=effective_tax_rate, + ) + + # Actually collect the taxes. + agent.state["inventory"]["Coin"] -= effective_taxes + net_tax_revenue += effective_taxes + + self.last_income.append(float(income)) + self.last_marginal_rate.append(float(marginal_rate)) + self.last_effective_tax_rate.append(effective_tax_rate) + + self.all_effective_tax_rates.append(effective_tax_rate) + self._occupancy["{:03d}".format(int(self.income_bin(income)))] += 1 + + self.total_collected_taxes += float(net_tax_revenue) + + lump_sum = net_tax_revenue / self.n_agents + for agent in self.world.agents: + agent.state["inventory"]["Coin"] += lump_sum + tax_dict[str(agent.idx)]["lump_sum"] = float(lump_sum) + self.last_coin[agent.idx] = float(agent.total_endowment("Coin")) + + self.taxes.append(tax_dict) + + # Pre-compute some things that will be useful for generating observations. + self._last_income_obs = np.array(self.last_income) / self.period + self._last_income_obs_sorted = self._last_income_obs[ + np.argsort(self._last_income_obs) + ] + + # Fold this period's tax data into the saez buffer. + if self.tax_model == "saez": + self._update_saez_buffer(tax_dict) + + # Required methods for implementing components + # -------------------------------------------- + + def get_n_actions(self, agent_cls_name): + """ + See base_component.py for detailed description. + + If using the "model_wrapper" tax model and taxes are enabled, the planner's + action space includes an action subspace for each of the tax brackets. Each + such action space has as many actions as there are discretized tax rates. + """ + # Only the planner takes actions through this component. + if agent_cls_name == "BasicPlanner": + if self.tax_model == "model_wrapper" and not self.disable_taxes: + # For every bracket, the planner can select one of the discretized + # tax rates. + return [ + ("TaxIndexBracket_{:03d}".format(int(r)), self.n_disc_rates) + for r in self.bracket_cutoffs + ] + + # Return 0 (no added actions) if the other conditions aren't met. + return 0 + + def get_additional_state_fields(self, agent_cls_name): + """This component does not add any agent state fields.""" + return {} + + def component_step(self): + """ + See base_component.py for detailed description. + + On the first day of each tax period, update taxes. On the last day, enact them. + """ + + # 1. On the first day of a new tax period: Set up the taxes for this period. + if self.tax_cycle_pos == 1: + if self.tax_model == "model_wrapper": + self.set_new_period_rates_model() + + if self.tax_model == "saez": + self.compute_and_set_new_period_rates_from_saez_formula() + + # (cache this for faster obs generation) + self._curr_rates_obs = np.array(self.curr_marginal_rates) + + # 2. On the last day of the tax period: Get $-taxes AND update agent endowments. + if self.tax_cycle_pos >= self.period: + self.enact_taxes() + self.tax_cycle_pos = 0 + + else: + self.taxes.append([]) + + # increment timestep. + self.tax_cycle_pos += 1 + + def generate_observations(self): + """ + See base_component.py for detailed description. + + Agents observe where in the tax period cycle they are, information about the + last period's incomes, and the current marginal tax rates, including the + marginal rate that will apply to their next unit of income. + + The planner observes the same type of information, but for all the agents. It + also sees, for each agent, their marginal tax rate and reported income from + the previous tax period. + """ + is_tax_day = float(self.tax_cycle_pos >= self.period) + is_first_day = float(self.tax_cycle_pos == 1) + tax_phase = self.tax_cycle_pos / self.period + + obs = dict() + + obs[self.world.planner.idx] = dict( + is_tax_day=is_tax_day, + is_first_day=is_first_day, + tax_phase=tax_phase, + last_incomes=self._last_income_obs_sorted, + curr_rates=self._curr_rates_obs, + ) + + for agent in self.world.agents: + i = agent.idx + k = str(i) + + curr_marginal_rate = self.marginal_rate( + agent.total_endowment("Coin") - self.last_coin[i] + ) + + obs[k] = dict( + is_tax_day=is_tax_day, + is_first_day=is_first_day, + tax_phase=tax_phase, + last_incomes=self._last_income_obs_sorted, + curr_rates=self._curr_rates_obs, + marginal_rate=curr_marginal_rate, + ) + + obs["p" + k] = dict( + last_income=self._last_income_obs[i], + last_marginal_rate=self.last_marginal_rate[i], + curr_marginal_rate=curr_marginal_rate, + ) + + return obs + + def generate_masks(self, completions=0): + """ + See base_component.py for detailed description. + + Masks only apply to the planner and if tax_model == "model_wrapper" and taxes + are enabled. + All tax actions are masked (so, only NO-OPs can be sampled) on all timesteps + except when self.tax_cycle_pos==1 (meaning a new tax period is starting). + When self.tax_cycle_pos==1, tax actions are masked in order to enforce any + tax annealing. + """ + if ( + completions != self._last_completions + and self.tax_annealing_schedule is not None + ): + self._last_completions = int(completions) + self._annealed_rate_max = annealed_tax_limit( + completions, + self._annealing_warmup, + self._annealing_slope, + self.rate_max, + ) + + if self.disable_taxes: + return {} + + if self.tax_model == "model_wrapper": + # No annealing. Generate masks using default method. + if self.tax_annealing_schedule is None: + if self._planner_masks is None: + masks = super().generate_masks(completions=completions) + self._planner_masks = dict( + new_taxes=deepcopy(masks[self.world.planner.idx]), + zeros={ + k: np.zeros_like(v) + for k, v in masks[self.world.planner.idx].items() + }, + ) + + # No need to recompute. Use the cached masks. + masks = dict() + if self.tax_cycle_pos != 1 or self.disable_taxes: + # Apply zero masks for any timestep where taxes + # are not going to be updated. + masks[self.world.planner.idx] = self._planner_masks["zeros"] + else: + masks[self.world.planner.idx] = self._planner_masks["new_taxes"] + + # Doing annealing. + else: + # Figure out what the masks should be this episode. + if self._planner_masks is None: + planner_masks = { + k: annealed_tax_mask( + completions, + self._annealing_warmup, + self._annealing_slope, + tax_values, + ) + for k, tax_values in self._planner_tax_val_dict.items() + } + self._planner_masks = dict( + new_taxes=deepcopy(planner_masks), + zeros={k: np.zeros_like(v) for k, v in planner_masks.items()}, + ) + + # No need to recompute. Use the cached masks. + masks = dict() + if self.tax_cycle_pos != 1 or self.disable_taxes: + # Apply zero masks for any timestep where taxes + # are not going to be updated. + masks[self.world.planner.idx] = self._planner_masks["zeros"] + else: + masks[self.world.planner.idx] = self._planner_masks["new_taxes"] + + # We are not using a learned planner. Generate masks by the default method. + else: + masks = super().generate_masks(completions=completions) + + return masks + + # For non-required customization + # ------------------------------ + + def additional_reset_steps(self): + """ + See base_component.py for detailed description. + + Reset trackers. + """ + self.curr_rate_indices = [0 for _ in range(self.n_brackets)] + + self.tax_cycle_pos = 1 + self.last_coin = [ + float(agent.total_endowment("Coin")) for agent in self.world.agents + ] + self.last_income = [0 for _ in range(self.n_agents)] + self.last_marginal_rate = [0 for _ in range(self.n_agents)] + self.last_effective_tax_rate = [0 for _ in range(self.n_agents)] + + self._curr_rates_obs = np.array(self.curr_marginal_rates) + self._last_income_obs = np.array(self.last_income) / self.period + self._last_income_obs_sorted = self._last_income_obs[ + np.argsort(self._last_income_obs) + ] + + self.taxes = [] + self.total_collected_taxes = 0 + self.all_effective_tax_rates = [] + self._schedules = {"{:03d}".format(int(r)): [] for r in self.bracket_cutoffs} + self._occupancy = {"{:03d}".format(int(r)): 0 for r in self.bracket_cutoffs} + self._planner_masks = None + + if self.tax_model == "saez": + self.curr_bracket_tax_rates = np.array(self.running_avg_tax_rates) + + def get_metrics(self): + """ + See base_component.py for detailed description. + + Return metrics related to bracket rates, bracket occupancy, and tax collection. + """ + out = dict() + + n_observed_incomes = np.maximum(1, np.sum(list(self._occupancy.values()))) + for c in self.bracket_cutoffs: + k = "{:03d}".format(int(c)) + out["avg_bracket_rate/{}".format(k)] = np.mean(self._schedules[k]) + out["bracket_occupancy/{}".format(k)] = ( + self._occupancy[k] / n_observed_incomes + ) + + if not self.disable_taxes: + out["avg_effective_tax_rate"] = np.mean(self.all_effective_tax_rates) + out["total_collected_taxes"] = float(self.total_collected_taxes) + + # Indices of richest and poorest agents. + agent_coin_endows = np.array( + [agent.total_endowment("Coin") for agent in self.world.agents] + ) + idx_poor = np.argmin(agent_coin_endows) + idx_rich = np.argmax(agent_coin_endows) + + tax_days = self.taxes[(self.period - 1) :: self.period] + for i, tag in zip([idx_poor, idx_rich], ["poorest", "richest"]): + total_income = np.maximum( + 0, [tax_day[str(i)]["income"] for tax_day in tax_days] + ).sum() + total_tax_paid = np.sum( + [tax_day[str(i)]["tax_paid"] for tax_day in tax_days] + ) + # Report the overall tax rate over the episode + # for the richest and poorest agents. + out["avg_tax_rate/{}".format(tag)] = total_tax_paid / np.maximum( + 0.001, total_income + ) + + if self.tax_model == "saez": + # Include the running estimate of elasticity. + out["saez/estimated_elasticity"] = self.elas_tm1 + + return out + + def get_dense_log(self): + """ + Log taxes. + + Returns: + taxes (list): A list of tax collections. Each entry corresponds to a single + timestep. Entries are empty except for timesteps where a tax period + ended and taxes were collected. For those timesteps, each entry + contains the tax schedule, each agent's reported income, tax paid, + and redistribution received. + Returns None if taxes are disabled. + """ + if self.disable_taxes: + return None + return self.taxes diff --git a/ai_economist/foundation/components/simple_labor.py b/ai_economist/foundation/components/simple_labor.py new file mode 100644 index 0000000..3b5adc1 --- /dev/null +++ b/ai_economist/foundation/components/simple_labor.py @@ -0,0 +1,134 @@ +# Copyright (c) 2021, salesforce.com, inc. +# All rights reserved. +# SPDX-License-Identifier: BSD-3-Clause +# For full license text, see the LICENSE file in the repo root +# or https://opensource.org/licenses/BSD-3-Clause + +import numpy as np + +from ai_economist.foundation.base.base_component import ( + BaseComponent, + component_registry, +) + + +@component_registry.add +class SimpleLabor(BaseComponent): + """ + Allows Agents to select a level of labor, which earns income based on skill. + + Labor is "simple" because this simplifies labor to a choice along a 1D axis. More + concretely, this component adds 100 labor actions, each representing a choice of + how many hours to work, e.g. action 50 represents doing 50 hours of work; each + Agent earns income proportional to the product of its labor amount (representing + hours worked) and its skill (representing wage), with higher skill and higher labor + yielding higher income. + + This component is intended to be used with the 'PeriodicBracketTax' component and + the 'one-step-economy' scenario. + + Args: + mask_first_step (bool): Defaults to True. If True, masks all non-0 labor + actions on the first step of the environment. When combined with the + intended component/scenario, the first env step is used to set taxes + (via the 'redistribution' component) and the second step is used to + select labor (via this component). + payment_max_skill_multiplier (float): When determining the skill level of + each Agent, sampled skills are clipped to this maximum value. + """ + + name = "SimpleLabor" + required_entities = ["Coin"] + agent_subclasses = ["BasicMobileAgent"] + + def __init__( + self, + *base_component_args, + mask_first_step=True, + payment_max_skill_multiplier=3, + pareto_param=4.0, + **base_component_kwargs + ): + super().__init__(*base_component_args, **base_component_kwargs) + + # This defines the size of the action space (the max # hours an agent can work). + self.num_labor_hours = 100 # max 100 hours + + assert isinstance(mask_first_step, bool) + self.mask_first_step = mask_first_step + + self.is_first_step = True + self.common_mask_on = { + agent.idx: np.ones((self.num_labor_hours,)) for agent in self.world.agents + } + self.common_mask_off = { + agent.idx: np.zeros((self.num_labor_hours,)) for agent in self.world.agents + } + + # Skill distribution + self.pareto_param = float(pareto_param) + assert self.pareto_param > 0 + self.payment_max_skill_multiplier = float(payment_max_skill_multiplier) + pmsm = self.payment_max_skill_multiplier + num_agents = len(self.world.agents) + # Generate a batch (1000) of num_agents (sorted/clipped) Pareto samples. + pareto_samples = np.random.pareto(4, size=(1000, num_agents)) + clipped_skills = np.minimum(pmsm, (pmsm - 1) * pareto_samples + 1) + sorted_clipped_skills = np.sort(clipped_skills, axis=1) + # The skill level of the i-th skill-ranked agent is the average of the + # i-th ranked samples throughout the batch. + self.skills = sorted_clipped_skills.mean(axis=0) + + def get_additional_state_fields(self, agent_cls_name): + if agent_cls_name == "BasicMobileAgent": + return {"skill": 0, "production": 0} + return {} + + def additional_reset_steps(self): + self.is_first_step = True + for agent in self.world.agents: + agent.state["skill"] = self.skills[agent.idx] + + def get_n_actions(self, agent_cls_name): + if agent_cls_name == "BasicMobileAgent": + return self.num_labor_hours + return None + + def generate_masks(self, completions=0): + if self.is_first_step: + self.is_first_step = False + if self.mask_first_step: + return self.common_mask_off + + return self.common_mask_on + + def component_step(self): + + for agent in self.world.get_random_order_agents(): + + action = agent.get_component_action(self.name) + + if action == 0: # NO-OP. + # Agent is not interacting with this component. + continue + + if 1 <= action <= self.num_labor_hours: # set reopening phase + + hours_worked = action # NO-OP is 0 hours. + agent.state["endogenous"]["Labor"] = hours_worked + + payoff = hours_worked * agent.state["skill"] + agent.state["production"] += payoff + agent.inventory["Coin"] += payoff + + else: + # If action > num_labor_hours, this is an error. + raise ValueError + + def generate_observations(self): + obs_dict = dict() + for agent in self.world.agents: + obs_dict[str(agent.idx)] = { + "skill": agent.state["skill"] / self.payment_max_skill_multiplier + } + return obs_dict diff --git a/ai_economist/foundation/components/utils.py b/ai_economist/foundation/components/utils.py new file mode 100644 index 0000000..d9418fb --- /dev/null +++ b/ai_economist/foundation/components/utils.py @@ -0,0 +1,115 @@ +# Copyright (c) 2020, salesforce.com, inc. +# All rights reserved. +# SPDX-License-Identifier: BSD-3-Clause +# For full license text, see the LICENSE file in the repo root +# or https://opensource.org/licenses/BSD-3-Clause + +import numpy as np + + +def annealed_tax_limit(completions, warmup_period, slope, final_max_tax_value=1.0): + """ + Compute the maximum tax rate available at this stage of tax annealing. + + This function uses the number of episode completions and the annealing schedule + (warmup_period, slope, & final_max_tax_value) to determine what the maximum tax + rate can be. + This type of annealing allows for a tax curriculum where earlier episodes are + restricted to lower tax rates. As more episodes are played, higher tax values are + allowed. + + Args: + completions (int): Number of times the environment has completed an episode. + Expected to be >= 0. + warmup_period (int): Until warmup_period completions, only allow 0 tax. Using + a negative value will enable non-0 taxes at 0 environment completions. + slope (float): After warmup_period completions, percentage of full tax value + unmasked with each new completion. + final_max_tax_value (float): The maximum tax value at the end of annealing. + + Returns: + A scalar value indicating the maximum tax at this stage of annealing. + + Example: + >> WARMUP = 100 + >> SLOPE = 0.01 + >> annealed_tax_limit(0, WARMUP, SLOPE) + 0.0 + >> annealed_tax_limit(100, WARMUP, SLOPE) + 0.0 + >> annealed_tax_limit(150, WARMUP, SLOPE) + 0.5 + >> annealed_tax_limit(200, WARMUP, SLOPE) + 1.0 + >> annealed_tax_limit(1000, WARMUP, SLOPE) + 1.0 + """ + # What percentage of the full range is currently visible + # (between 0 [only 0 tax] and 1 [all taxes visible]) + percentage_visible = np.maximum( + 0.0, np.minimum(1.0, slope * (completions - warmup_period)) + ) + + # Determine the highest allowable tax, + # given the current position in the annealing schedule + current_max_tax = percentage_visible * final_max_tax_value + + return current_max_tax + + +def annealed_tax_mask(completions, warmup_period, slope, tax_values): + """ + Generate a mask applied to a set of tax values for the purpose of tax annealing. + + This function uses the number of episode completions and the annealing schedule + to determine which of the tax values are considered valid. The most extreme + tax/subsidy values are unmasked last. Zero tax is always unmasked (i.e. always + valid). + This type of annealing allows for a tax curriculum where earlier episodes are + restricted to lower tax rates. As more episodes are played, higher tax values are + allowed. + + Args: + completions (int): Number of times the environment has completed an episode. + Expected to be >= 0. + warmup_period (int): Until warmup_period completions, only allow 0 tax. Using + a negative value will enable non-0 taxes at 0 environment completions. + slope (float): After warmup_period completions, percentage of full tax value + unmasked with each new completion. + tax_values (list): The list of tax values associated with each action to + which this mask will apply. + + Returns: + A binary mask with same shape as tax_values, indicating which tax values are + currently valid. + + Example: + >> WARMUP = 100 + >> SLOPE = 0.01 + >> TAX_VALUES = [0.0, 0.25, 0.50, 0.75, 1.0] + >> annealed_tax_limit(0, WARMUP, SLOPE, TAX_VALUES) + [0, 0, 0, 0, 0] + >> annealed_tax_limit(100, WARMUP, SLOPE, TAX_VALUES) + [0, 0, 0, 0, 0] + >> annealed_tax_limit(150, WARMUP, SLOPE, TAX_VALUES) + [1, 1, 1, 0, 0] + >> annealed_tax_limit(200, WARMUP, SLOPE, TAX_VALUES) + [1, 1, 1, 1, 1] + >> annealed_tax_limit(1000, WARMUP, SLOPE, TAX_VALUES) + [1, 1, 1, 1, 1] + """ + # Infer the most extreme tax level from the supplied tax values. + abs_tax = np.abs(tax_values) + full_tax_amount = np.max(abs_tax) + + # Determine the highest allowable tax, given the current position + # in the annealing schedule + max_absolute_visible_tax = annealed_tax_limit( + completions, warmup_period, slope, full_tax_amount + ) + + # Return a binary mask to allow for taxes + # at or below the highest absolute visible tax + return np.less_equal(np.abs(tax_values), max_absolute_visible_tax).astype( + np.float32 + ) diff --git a/ai_economist/foundation/entities/__init__.py b/ai_economist/foundation/entities/__init__.py new file mode 100644 index 0000000..58a385e --- /dev/null +++ b/ai_economist/foundation/entities/__init__.py @@ -0,0 +1,9 @@ +# Copyright (c) 2020, salesforce.com, inc. +# All rights reserved. +# SPDX-License-Identifier: BSD-3-Clause +# For full license text, see the LICENSE file in the repo root +# or https://opensource.org/licenses/BSD-3-Clause + +from .endogenous import endogenous_registry +from .landmarks import landmark_registry +from .resources import resource_registry diff --git a/ai_economist/foundation/entities/endogenous.py b/ai_economist/foundation/entities/endogenous.py new file mode 100644 index 0000000..b6eb42a --- /dev/null +++ b/ai_economist/foundation/entities/endogenous.py @@ -0,0 +1,36 @@ +# Copyright (c) 2020, salesforce.com, inc. +# All rights reserved. +# SPDX-License-Identifier: BSD-3-Clause +# For full license text, see the LICENSE file in the repo root +# or https://opensource.org/licenses/BSD-3-Clause + +from ai_economist.foundation.base.registrar import Registry + + +class Endogenous: + """Base class for endogenous entity classes. + + Endogenous entities are those that, conceptually, describe the internal state + of an agent. This provides a convenient way to separate physical entities (which + may exist in the world, be exchanged among agents, or are otherwise in principal + observable by others) from endogenous entities (such as the amount of labor + effort an agent has experienced). + + Endogenous entities are registered in the "endogenous" portion of an agent's + state and should only be observable by the agent itself. + """ + + name = None + + def __init__(self): + assert self.name is not None + + +endogenous_registry = Registry(Endogenous) + + +@endogenous_registry.add +class Labor(Endogenous): + """Labor accumulated through working. Included in all environments by default.""" + + name = "Labor" diff --git a/ai_economist/foundation/entities/landmarks.py b/ai_economist/foundation/entities/landmarks.py new file mode 100644 index 0000000..7f01457 --- /dev/null +++ b/ai_economist/foundation/entities/landmarks.py @@ -0,0 +1,88 @@ +# Copyright (c) 2020, salesforce.com, inc. +# All rights reserved. +# SPDX-License-Identifier: BSD-3-Clause +# For full license text, see the LICENSE file in the repo root +# or https://opensource.org/licenses/BSD-3-Clause + +import numpy as np + +from ai_economist.foundation.base.registrar import Registry +from ai_economist.foundation.entities.resources import resource_registry + + +class Landmark: + """Base class for Landmark entity classes. + + Landmark classes describe the entities that exist exclusively in the environment + world. In other words, they represent entities that should not be included in an + agent's inventory and are only observable through observations from the + spatial world. + + Landmark classes describe the following properties: + ownable: If each instance of the landmark belongs to an agent. For example, a + "House" is ownable and belongs to the agent that constructs it whereas + "Water" is not ownable. + solid: If the landmark creates a physical barrier to movement (that is, + if agents are prevented from occupying cells with the landmark). + Importantly, if the landmark is ownable, the agent that owns a given + landmark can occupy its cell even if the landmark is solid. + """ + + name = None + color = None # array of RGB values [0 - 1] + ownable = None + solid = True # Solid = Cannot be passed through + # (unless it is owned by the agent trying to pass through) + + def __init__(self): + assert self.name is not None + assert self.color is not None + assert self.ownable is not None + + # No agent can pass through this landmark + self.blocking = self.solid and not self.ownable + + # Only the agent that owns this landmark can pass through it + self.private = self.solid and self.ownable + + # This landmark does not belong to any agent and it does not inhibit movement + self.public = not self.solid and not self.ownable + + +landmark_registry = Registry(Landmark) + +# Registering each collectible resource's source block +# allows treating source blocks in a specific way +for resource_name in resource_registry.entries: + resource = resource_registry.get(resource_name) + if not resource.collectible: + continue + + @landmark_registry.add + class SourceBlock(Landmark): + """Special Landmark for generating resources. Not ownable. Not solid.""" + + name = "{}SourceBlock".format(resource.name) + color = np.array(resource.color) + ownable = False + solid = False + + +@landmark_registry.add +class House(Landmark): + """House landmark. Ownable. Solid.""" + + name = "House" + color = np.array([220, 20, 220]) / 255.0 + ownable = True + solid = True + + +@landmark_registry.add +class Water(Landmark): + """Water Landmark. Not ownable. Solid.""" + + name = "Water" + color = np.array([50, 50, 250]) / 255.0 + ownable = False + solid = True diff --git a/ai_economist/foundation/entities/resources.py b/ai_economist/foundation/entities/resources.py new file mode 100644 index 0000000..fe6693c --- /dev/null +++ b/ai_economist/foundation/entities/resources.py @@ -0,0 +1,84 @@ +# Copyright (c) 2020, salesforce.com, inc. +# All rights reserved. +# SPDX-License-Identifier: BSD-3-Clause +# For full license text, see the LICENSE file in the repo root +# or https://opensource.org/licenses/BSD-3-Clause + +import numpy as np + +from ai_economist.foundation.base.registrar import Registry + + +class Resource: + """Base class for Resource entity classes. + + Resource classes describe entities that can be a part of an agent's inventory. + + Resources can also be a part of the world as collectible entities: for each + Resource class with Resource.collectible=True, a complementary + ResourceSourceBlock Landmark class will be created in landmarks.py. For each + collectible resource in the environment, the world map will include a resource + source block channel (representing landmarks where collectible resources are + generated) and a resource channel (representing locations where collectible + resources have generated). + """ + + name = None + color = None # array of RGB values [0 - 1] + collectible = None # Is this something that exists in the world? + # (versus something that can only be owned) + craft_recp = None # dict of recource name and amount + craft_labour_base= 0.0 + + def __init__(self): + assert self.name is not None + assert self.color is not None + assert self.collectible is not None + + +resource_registry = Registry(Resource) + + +@resource_registry.add +class Wood(Resource): + """Wood resource. collectible.""" + + name = "Wood" + color = np.array([107, 143, 113]) / 255.0 + collectible = True + + +@resource_registry.add +class Stone(Resource): + """Stone resource. collectible.""" + + name = "Stone" + color = np.array([241, 233, 219]) / 255.0 + collectible = True + + +@resource_registry.add +class Coin(Resource): + """Coin resource. Included in all environments by default. Not collectible.""" + + name = "Coin" + color = np.array([229, 211, 82]) / 255.0 + collectible = False + +@resource_registry.add +class RawGem(Resource): + """Raw Gem that can be processed further""" + + name = "Raw_Gem" + color = np.array([241, 233, 219]) / 255.0 + collectible = True + +@resource_registry.add +class Gem(Resource): + """Proccesed Gem. Craftable.""" + + name = "Gem" + color = np.array([241, 233, 219]) / 255.0 + collectible = False + craft_recp= {"Raw_Gem": 1} + craft_labour_base= 1 \ No newline at end of file diff --git a/ai_economist/foundation/env_wrapper.py b/ai_economist/foundation/env_wrapper.py new file mode 100644 index 0000000..29db95e --- /dev/null +++ b/ai_economist/foundation/env_wrapper.py @@ -0,0 +1,418 @@ +# Copyright (c) 2021, salesforce.com, inc. +# All rights reserved. +# SPDX-License-Identifier: BSD-3-Clause +# For full license text, see the LICENSE file in the repo root +# or https://opensource.org/licenses/BSD-3-Clause + +""" +The env wrapper class +""" + +import logging + +import GPUtil + +try: + num_gpus_available = len(GPUtil.getAvailable()) + print(f"Inside env_wrapper.py: {num_gpus_available} GPUs are available.") + if num_gpus_available == 0: + print("No GPUs found! Running the simulation on a CPU.") + else: + from warp_drive.managers.data_manager import CUDADataManager + from warp_drive.managers.function_manager import ( + CUDAEnvironmentReset, + CUDAFunctionManager, + ) +except ModuleNotFoundError: + print( + "Warning: The 'WarpDrive' package is not found and cannot be used! " + "If you wish to use WarpDrive, please run " + "'pip install rl-warp-drive' first." + ) +except ValueError: + print("No GPUs found! Running the simulation on a CPU.") + +import numpy as np +from gym.spaces import Box, Dict, Discrete, MultiDiscrete + +BIG_NUMBER = 1e20 + + +def recursive_obs_dict_to_spaces_dict(obs): + """Recursively return the observation space dictionary + for a dictionary of observations + + Args: + obs (dict): A dictionary of observations keyed by agent index + for a multi-agent environment + + Returns: + Dict: A dictionary (space.Dict) of observation spaces + """ + assert isinstance(obs, dict) + dict_of_spaces = {} + for k, v in obs.items(): + + # list of lists are listified np arrays + _v = v + if isinstance(v, list): + _v = np.array(v) + elif isinstance(v, (int, np.integer, float, np.floating)): + _v = np.array([v]) + + # assign Space + if isinstance(_v, np.ndarray): + x = float(BIG_NUMBER) + box = Box(low=-x, high=x, shape=_v.shape, dtype=_v.dtype) + low_high_valid = (box.low < 0).all() and (box.high > 0).all() + + # This loop avoids issues with overflow to make sure low/high are good. + while not low_high_valid: + x = x // 2 + box = Box(low=-x, high=x, shape=_v.shape, dtype=_v.dtype) + low_high_valid = (box.low < 0).all() and (box.high > 0).all() + + dict_of_spaces[k] = box + + elif isinstance(_v, dict): + dict_of_spaces[k] = recursive_obs_dict_to_spaces_dict(_v) + else: + raise TypeError + return Dict(dict_of_spaces) + + +class FoundationEnvWrapper: + """ + The environment wrapper class for Foundation. + This wrapper determines whether the environment reset and steps happen on the + CPU or the GPU, and proceeds accordingly. + If the environment runs on the CPU, the reset() and step() calls also occur on + the CPU. + If the environment runs on the GPU, only the first reset() happens on the CPU, + all the relevant data is copied over the GPU after, and the subsequent steps + all happen on the GPU. + """ + + def __init__( + self, + env_obj=None, + env_name=None, + env_config=None, + num_envs=1, + use_cuda=False, + env_registrar=None, + event_messenger=None, + process_id=0, + ): + """ + 'env_obj': an environment object + 'env_name': an environment name that is registered on the + WarpDrive environment registrar + 'env_config': environment configuration to instantiate + an environment from the registrar + 'use_cuda': if True, step through the environment on the GPU, else on the CPU + 'num_envs': the number of parallel environments to instantiate. Note: this is + only relevant when use_cuda is True + 'env_registrar': EnvironmentRegistrar object + it provides the customized env info (like src path) for the build + 'event_messenger': multiprocessing Event to sync up the build + when using multiple processes + 'process_id': id of the process running WarpDrive + """ + # Need to pass in an environment instance + if env_obj is not None: + self.env = env_obj + else: + assert ( + env_name is not None + and env_config is not None + and env_registrar is not None + ) + self.env = env_registrar.get(env_name, use_cuda)(**env_config) + + self.n_agents = self.env.num_agents + self.episode_length = self.env.episode_length + + assert self.env.name + self.name = self.env.name + + # Add observation space to the env + # -------------------------------- + # Note: when the collated agent "a" is present, add obs keys + # for each individual agent to the env + # and remove the collated agent "a" from the observation + obs = self.obs_at_reset() + self.env.observation_space = recursive_obs_dict_to_spaces_dict(obs) + + # Add action space to the env + # --------------------------- + self.env.action_space = {} + for agent_id in range(len(self.env.world.agents)): + if self.env.world.agents[agent_id].multi_action_mode: + self.env.action_space[str([agent_id])] = MultiDiscrete( + self.env.get_agent(str(agent_id)).action_spaces + ) + else: + self.env.action_space[str(agent_id)] = Discrete( + self.env.get_agent(str(agent_id)).action_spaces + ) + self.env.action_space[str(agent_id)].dtype = np.int32 + + if self.env.world.planner.multi_action_mode: + self.env.action_space["p"] = MultiDiscrete( + self.env.get_agent("p").action_spaces + ) + else: + self.env.action_space["p"] = Discrete(self.env.get_agent("p").action_spaces) + self.env.action_space["p"].dtype = np.int32 + + # Ensure the observation and action spaces share the same keys + assert set(self.env.observation_space.keys()) == set( + self.env.action_space.keys() + ) + + # CUDA-specific initializations + # ----------------------------- + # Flag to determine whether to use CUDA or not + self.use_cuda = use_cuda + if self.use_cuda: + assert len(GPUtil.getAvailable()) > 0, ( + "The env wrapper needs a GPU to run" " when use_cuda is True!" + ) + assert hasattr(self.env, "use_cuda") + assert hasattr(self.env, "cuda_data_manager") + assert hasattr(self.env, "cuda_function_manager") + + assert hasattr(self.env.world, "use_cuda") + assert hasattr(self.env.world, "cuda_data_manager") + assert hasattr(self.env.world, "cuda_function_manager") + self.env.use_cuda = use_cuda + self.env.world.use_cuda = self.use_cuda + + # Flag to determine where the reset happens (host or device) + # First reset is always on the host (CPU), and subsequent resets are on + # the device (GPU) + self.reset_on_host = True + + # Steps specific to GPU runs + # -------------------------- + if self.use_cuda: + logging.info("USING CUDA...") + + # Number of environments to run in parallel + assert num_envs >= 1 + self.n_envs = num_envs + + logging.info("Initializing the CUDA data manager...") + self.cuda_data_manager = CUDADataManager( + num_agents=self.n_agents, + episode_length=self.episode_length, + num_envs=self.n_envs, + ) + + logging.info("Initializing the CUDA function manager...") + self.cuda_function_manager = CUDAFunctionManager( + num_agents=int(self.cuda_data_manager.meta_info("n_agents")), + num_envs=int(self.cuda_data_manager.meta_info("n_envs")), + process_id=process_id, + ) + self.cuda_function_manager.compile_and_load_cuda( + env_name=self.name, + template_header_file="template_env_config.h", + template_runner_file="template_env_runner.cu", + customized_env_registrar=env_registrar, + event_messenger=event_messenger, + ) + + # Register the CUDA step() function for the env + # Note: generate_observation() and compute_reward() + # should be part of the step function itself + step_function = f"Cuda{self.name}Step" + self.cuda_function_manager.initialize_functions([step_function]) + self.env.cuda_step = self.cuda_function_manager.get_function(step_function) + + # Register additional cuda functions (other than the scenario step) + # Component step + # Create a cuda_component_step dictionary + self.env.world.cuda_component_step = {} + for component in self.env.components: + self.cuda_function_manager.initialize_functions( + ["Cuda" + component.name + "Step"] + ) + self.env.world.cuda_component_step[ + component.name + ] = self.cuda_function_manager.get_function( + "Cuda" + component.name + "Step" + ) + + # Compute reward + self.cuda_function_manager.initialize_functions(["CudaComputeReward"]) + self.env.cuda_compute_reward = self.cuda_function_manager.get_function( + "CudaComputeReward" + ) + + # Add wrapper attributes for use within env + self.env.cuda_data_manager = self.cuda_data_manager + self.env.cuda_function_manager = self.cuda_function_manager + + # Register the env resetter + self.env_resetter = CUDAEnvironmentReset( + function_manager=self.cuda_function_manager + ) + + # Add to self.env.world for use in components + self.env.world.cuda_data_manager = self.cuda_data_manager + self.env.world.cuda_function_manager = self.cuda_function_manager + + def reset_all_envs(self): + """ + Reset the state of the environment to initialize a new episode. + if self.reset_on_host is True: + calls the CPU env to prepare and return the initial state + if self.use_cuda is True: + if self.reset_on_host is True: + expands initial state to parallel example_envs and push to GPU once + sets self.reset_on_host = False + else: + calls device hard reset managed by the CUDAResetter + """ + self.env.world.timestep = 0 + + if self.reset_on_host: + # Produce observation + obs = self.obs_at_reset() + else: + assert self.use_cuda + + if self.use_cuda: # GPU version + if self.reset_on_host: + + # Helper function to repeat data across the env dimension + def repeat_across_env_dimension(array, num_envs): + return np.stack([array for _ in range(num_envs)], axis=0) + + # Copy host data and tensors to device + # Note: this happens only once after the first reset on the host + + scenario_and_components = [self.env] + self.env.components + + for item in scenario_and_components: + # Add env dimension to data + # if "save_copy_and_apply_at_reset" is True + data_dictionary = item.get_data_dictionary() + tensor_dictionary = item.get_tensor_dictionary() + for key in data_dictionary: + if data_dictionary[key]["attributes"][ + "save_copy_and_apply_at_reset" + ]: + data_dictionary[key]["data"] = repeat_across_env_dimension( + data_dictionary[key]["data"], self.n_envs + ) + + for key in tensor_dictionary: + if tensor_dictionary[key]["attributes"][ + "save_copy_and_apply_at_reset" + ]: + tensor_dictionary[key][ + "data" + ] = repeat_across_env_dimension( + tensor_dictionary[key]["data"], self.n_envs + ) + + self.cuda_data_manager.push_data_to_device(data_dictionary) + + self.cuda_data_manager.push_data_to_device( + tensor_dictionary, torch_accessible=True + ) + + # All subsequent resets happen on the GPU + self.reset_on_host = False + + # Return the obs + return obs + # Returns an empty dictionary for all subsequent resets on the GPU + # as arrays are modified in place + self.env_resetter.reset_when_done( + self.cuda_data_manager, mode="force_reset" + ) + return {} + return obs # CPU version + + def reset_only_done_envs(self): + """ + This function only works for GPU example_envs. + It will check all the running example_envs, + and only resets those example_envs that are observing done flag is True + """ + assert self.use_cuda and not self.reset_on_host, ( + "reset_only_done_envs() only works " + "for self.use_cuda = True and self.reset_on_host = False" + ) + + self.env_resetter.reset_when_done(self.cuda_data_manager, mode="if_done") + return {} + + def step_all_envs(self, actions=None): + """ + Step through all the environments' components and scenario + """ + if self.use_cuda: + # Step through each component + for component in self.env.components: + component.component_step() + + # Scenario step + self.env.scenario_step() + + # Compute rewards + self.env.generate_rewards() + + result = None # Do not return anything + else: + assert actions is not None, "Please provide actions to step with." + obs, rew, done, info = self.env.step(actions) + obs = self._reformat_obs(obs) + rew = self._reformat_rew(rew) + result = obs, rew, done, info + return result + + def obs_at_reset(self): + """ + Calls the (Python) env to reset and return the initial state + """ + obs = self.env.reset() + obs = self._reformat_obs(obs) + return obs + + def _reformat_obs(self, obs): + if "a" in obs: + # This means the env uses collated obs. + # Set each individual agent as obs keys for processing with WarpDrive. + for agent_id in range(self.env.n_agents): + obs[str(agent_id)] = {} + for key in obs["a"].keys(): + obs[str(agent_id)][key] = obs["a"][key][..., agent_id] + del obs["a"] # remove the key "a" + return obs + + def _reformat_rew(self, rew): + if "a" in rew: + # This means the env uses collated rew. + # Set each individual agent as rew keys for processing with WarpDrive. + assert isinstance(rew, dict) + for agent_id in range(self.env.n_agents): + rew[str(agent_id)] = rew["a"][agent_id] + del rew["a"] # remove the key "a" + return rew + + def reset(self): + """ + Alias for reset_all_envs() when CPU is used (conforms to gym-style) + """ + return self.reset_all_envs() + + def step(self, actions=None): + """ + Alias for step_all_envs() when CPU is used (conforms to gym-style) + """ + return self.step_all_envs(actions) diff --git a/ai_economist/foundation/scenarios/__init__.py b/ai_economist/foundation/scenarios/__init__.py new file mode 100644 index 0000000..f793d8d --- /dev/null +++ b/ai_economist/foundation/scenarios/__init__.py @@ -0,0 +1,14 @@ +# Copyright (c) 2020, salesforce.com, inc. +# All rights reserved. +# SPDX-License-Identifier: BSD-3-Clause +# For full license text, see the LICENSE file in the repo root +# or https://opensource.org/licenses/BSD-3-Clause + +from ai_economist.foundation.base.base_env import scenario_registry + +from .covid19 import covid19_env +from .one_step_economy import one_step_economy +from .simple_wood_and_stone import dynamic_layout, layout_from_file + +# Import files that add Scenario class(es) to scenario_registry +# ------------------------------------------------------------- diff --git a/ai_economist/foundation/scenarios/covid19/__init__.py b/ai_economist/foundation/scenarios/covid19/__init__.py new file mode 100644 index 0000000..fc6cee4 --- /dev/null +++ b/ai_economist/foundation/scenarios/covid19/__init__.py @@ -0,0 +1,5 @@ +# Copyright (c) 2020, salesforce.com, inc. +# All rights reserved. +# SPDX-License-Identifier: BSD-3-Clause +# For full license text, see the LICENSE file in the repo root +# or https://opensource.org/licenses/BSD-3-Clause diff --git a/ai_economist/foundation/scenarios/covid19/covid19_build.cu b/ai_economist/foundation/scenarios/covid19/covid19_build.cu new file mode 100644 index 0000000..e5d5d48 --- /dev/null +++ b/ai_economist/foundation/scenarios/covid19/covid19_build.cu @@ -0,0 +1,13 @@ +// Copyright (c) 2021, salesforce.com, inc. +// All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// For full license text, see the LICENSE file in the repo root +// or https://opensource.org/licenses/BSD-3-Clause + +#ifndef CUDA_INCLUDES_COVID19_CONST_H_ +#define CUDA_INCLUDES_COVID19_CONST_H_ + +#include "../../components/covid19_components_step.cu" +#include "covid19_env_step.cu" + +#endif // CUDA_INCLUDES_COVID19_CONST_H_ diff --git a/ai_economist/foundation/scenarios/covid19/covid19_env.py b/ai_economist/foundation/scenarios/covid19/covid19_env.py new file mode 100644 index 0000000..c6c385a --- /dev/null +++ b/ai_economist/foundation/scenarios/covid19/covid19_env.py @@ -0,0 +1,1687 @@ +# Copyright (c) 2021, salesforce.com, inc. +# All rights reserved. +# SPDX-License-Identifier: BSD-3-Clause +# For full license text, see the LICENSE file in the repo root +# or https://opensource.org/licenses/BSD-3-Clause + +import json +import os +from datetime import datetime, timedelta + +import GPUtil +import numpy as np + +from ai_economist.foundation.base.base_env import BaseEnvironment, scenario_registry +from ai_economist.foundation.utils import verify_activation_code + +try: + num_gpus_available = len(GPUtil.getAvailable()) + print(f"Inside covid19_env.py: {num_gpus_available} GPUs are available.") + if num_gpus_available == 0: + print("No GPUs found! Running the simulation on a CPU.") + else: + from warp_drive.utils.constants import Constants + from warp_drive.utils.data_feed import DataFeed + + _OBSERVATIONS = Constants.OBSERVATIONS + _ACTIONS = Constants.ACTIONS + _REWARDS = Constants.REWARDS +except ModuleNotFoundError: + print( + "Warning: The 'WarpDrive' package is not found and cannot be used! " + "If you wish to use WarpDrive, please run " + "'pip install rl-warp-drive' first." + ) +except ValueError: + print("No GPUs found! Running the simulation on a CPU.") + + +@scenario_registry.add +class CovidAndEconomyEnvironment(BaseEnvironment): + """ + A simulation to model health and economy dynamics amidst the COVID-19 pandemic. + The environment comprising 51 agents (each agent corresponding to a US state and + Washington D.C.) and the Federal Government (planner). The state agents decide the + stringency level of the policy response to the pandemic, while the federal + government provides subsidies to eligible individuals. + + This simulation makes modeling assumptions. For details, see the technical paper: + https://arxiv.org/abs/2108.02904 + + Args: + use_real_world_data (bool): Replay what happened in the real world. + Real-world data comprises SIR (susceptible/infected/recovered), + unemployment, government policy, and vaccination numbers. + This setting also sets use_real_world_policies=True. + use_real_world_policies (bool): Run the environment with real-world policies + (stringency levels and subsidies). With this setting and + use_real_world_data=False, SIR and economy dynamics are still + driven by fitted models. + path_to_data_and_fitted_params (dirpath): Full path to the directory containing + the data, fitted parameters and model constants. This defaults to + "ai_economist/datasets/covid19_datasets/data_and_fitted_params". + For details on obtaining these parameters, please see the notebook + "ai-economist-foundation/ai_economist/datasets/covid19_datasets/ + gather_real_world_data_and_fit_parameters.ipynb". + start_date (string): Date (YYYY-MM-DD) to start the simulation. + pop_between_age_18_65 (float): Fraction of the population between ages 18-65. + This is the subset of the population whose employment/unemployment affects + economic productivity. + Range: 0 <= pop_between_age_18_65 <= 1. + infection_too_sick_to_work_rate (float): Fraction of people infected with + COVID-19. Infected people don't work. + Range: 0 <= infection_too_sick_to_work_rate <= 1 + risk_free_interest_rate (float): Percentage of interest paid by the federal + government to borrow money from the federal reserve for COVID-19 relief + (direct payments). Higher interest rates mean that direct payments + have a larger cost on the federal government's economic index. + Range: 0 <= risk_free_interest_rate + economic_reward_crra_eta (float): CRRA eta parameter for modeling the economic + reward non-linearity. + A useful reference: https://en.wikipedia.org/wiki/Isoelastic_utility + Range: 0 <= economic_reward_crra_eta + health_priority_scaling_agents (float): A factor indicating how much more the + states prioritize health (roughly speaking, loss of lives due to + opening up more) over the economy (roughly speaking, a loss in GDP + due to shutting down resulting in more unemployment) compared to the + real-world. + For example, a value of 1 corresponds to the real-world, while + a value of 2 means that states cared twice as much about public health + (preventing deaths), while a value of 0.5 means that states cared twice + as much about the economy (preventing GDP drops). + Range: 0 <= health_priority_scaling_agents + health_priority_scaling_planner (float): same as above, + but for the federal government. + Range: 0 <= health_priority_scaling_planner + """ + + def __init__( + self, + *base_env_args, + use_real_world_data=False, + use_real_world_policies=False, + path_to_data_and_fitted_params="", + start_date="2020-03-22", + pop_between_age_18_65=0.6, + infection_too_sick_to_work_rate=0.1, + risk_free_interest_rate=0.03, + economic_reward_crra_eta=2, + health_priority_scaling_agents=1, + health_priority_scaling_planner=1, + reward_normalization_factor=1, + **base_env_kwargs, + ): + verify_activation_code() + + # Used for datatype checks + self.np_float_dtype = np.float32 + self.np_int_dtype = np.int32 + + # Flag to use real-world data or the fitted models instead + self.use_real_world_data = use_real_world_data + # Flag to use real-world policies (actions) or the supplied actions instead + self.use_real_world_policies = use_real_world_policies + + # If we use real-world data, we also want to use the real-world policies + if self.use_real_world_data: + print( + "Using real-world data to initialize as well as to " + "step through the env." + ) + # Note: under this setting, the real_world policies are also used. + assert self.use_real_world_policies, ( + "Since the env. config. 'use_real_world_data' is True, please also " + "set 'use_real_world_policies' to True." + ) + else: + print( + "Using the real-world data to only initialize the env, " + "and using the fitted models to step through the env." + ) + + # Load real-world date + if path_to_data_and_fitted_params == "": + current_dir = os.path.dirname(__file__) + self.path_to_data_and_fitted_params = os.path.join( + current_dir, "../../../datasets/covid19_datasets/data_and_fitted_params" + ) + else: + self.path_to_data_and_fitted_params = path_to_data_and_fitted_params + + print( + "Loading real-world data from {}".format( + self.path_to_data_and_fitted_params + ) + ) + real_world_data_npz = np.load( + os.path.join(self.path_to_data_and_fitted_params, "real_world_data.npz") + ) + self._real_world_data = {} + for key in list(real_world_data_npz): + self._real_world_data[key] = real_world_data_npz[key] + + # Load fitted parameters + print( + "Loading fit parameters from {}".format(self.path_to_data_and_fitted_params) + ) + self.load_model_constants(self.path_to_data_and_fitted_params) + self.load_fitted_params(self.path_to_data_and_fitted_params) + + try: + self.start_date = datetime.strptime(start_date, self.date_format) + except ValueError: + print(f"Incorrect data format, should be {self.date_format}") + + # Start date should be beyond the date for which data is available + assert self.start_date >= self.policy_start_date + + # Compute a start date index based on policy start date + self.start_date_index = (self.start_date - self.policy_start_date).days + assert 0 <= self.start_date_index < len(self._real_world_data["policy"]) + + # For date logging (This will be overwritten in additional_reset_steps; + # see below) + self.current_date = None + + # When using real-world policy, limit the episode length + # to the length of the available policy. + if self.use_real_world_policies: + real_world_policy_length = ( + len(self._real_world_data["policy"]) - self.start_date_index + ) + print("Using real-world policies, ignoring external action inputs.") + assert base_env_kwargs["episode_length"] <= real_world_policy_length, ( + f"The real-world policies are only available for " + f"{real_world_policy_length} timesteps; so the 'episode_length' " + f"in the environment configuration can only be at most " + f"{real_world_policy_length}" + ) + else: + print("Using external action inputs.") + + # US states and populations + self.num_us_states = len(self.us_state_population) + + assert ( + base_env_kwargs["n_agents"] == self.num_us_states + ), "n_agents should be set to the number of US states, i.e., {}.".format( + self.num_us_states + ) + # Note: For a faster environment step time, we collate all the individual agents + # into a single agent index "a" and we flatten the component action masks too. + assert base_env_kwargs[ + "collate_agent_step_and_reset_data" + ], "The env. config 'collate_agent_step_and_reset_data' should be set to True." + super().__init__(*base_env_args, **base_env_kwargs) + + # Add attributes to self.world for use in components + self.world.us_state_population = self.us_state_population + self.world.us_population = self.us_population + self.world.start_date = self.start_date + self.world.n_stringency_levels = self.num_stringency_levels + self.world.use_real_world_policies = self.use_real_world_policies + if self.use_real_world_policies: + # Agent open/close stringency levels + self.world.real_world_stringency_policy = self._real_world_data["policy"][ + self.start_date_index : + ] + # Planner subsidy levels + self.world.real_world_subsidy = self._real_world_data["subsidy"][ + self.start_date_index : + ] + + # Policy --> Unemployment + # For accurately modeling the state-wise unemployment, we convolve + # the current stringency policy with a family of exponential filters + # with separate means (lambdas). + # This code sets up things we will use in `unemployment_step()`, + # which includes a detailed breakdown of how the unemployment model is + # implemented. + self.stringency_level_history = None + # Each filter captures a temporally extended response to a stringency change. + self.num_filters = len(self.conv_lambdas) + self.f_ts = np.tile( + np.flip(np.arange(self.filter_len), (0,))[None, None], + (1, self.num_filters, 1), + ).astype(self.np_float_dtype) + self.unemp_conv_filters = np.exp(-self.f_ts / self.conv_lambdas[None, :, None]) + # Each state weights these filters differently. + self.repeated_conv_weights = np.repeat( + self.grouped_convolutional_filter_weights.reshape( + self.num_us_states, self.num_filters + )[:, :, np.newaxis], + self.filter_len, + axis=-1, + ) + + # For manually modulating SIR/Unemployment parameters + self._beta_intercepts_modulation = 1 + self._beta_slopes_modulation = 1 + self._unemployment_modulation = 1 + + # Economy-related + # Interest rate for borrowing money from the federal reserve + self.risk_free_interest_rate = self.np_float_dtype(risk_free_interest_rate) + + # Compute each worker's daily productivity when at work (to match 2019 GDP) + # We assume the open/close stringency policy level was always at it's lowest + # value (i.e., 1) before the pandemic started. + num_unemployed_at_stringency_level_1 = self.unemployment_step( + np.ones(self.num_us_states) + ) + workforce = ( + self.us_population * pop_between_age_18_65 + - np.sum(num_unemployed_at_stringency_level_1) + ).astype(self.np_int_dtype) + workers_per_capita = (workforce / self.us_population).astype( + self.np_float_dtype + ) + gdp_per_worker = (self.gdp_per_capita / workers_per_capita).astype( + self.np_float_dtype + ) + self.num_days_in_an_year = 365 + self.daily_production_per_worker = ( + gdp_per_worker / self.num_days_in_an_year + ).astype(self.np_float_dtype) + + self.infection_too_sick_to_work_rate = self.np_float_dtype( + infection_too_sick_to_work_rate + ) + assert 0 <= self.infection_too_sick_to_work_rate <= 1 + + self.pop_between_age_18_65 = self.np_float_dtype(pop_between_age_18_65) + assert 0 <= self.pop_between_age_18_65 <= 1 + + # Compute max possible productivity values (used for agent reward normalization) + max_productivity_t = self.economy_step( + self.us_state_population, + np.zeros((self.num_us_states), dtype=self.np_int_dtype), + np.zeros((self.num_us_states), dtype=self.np_int_dtype), + num_unemployed_at_stringency_level_1, + infection_too_sick_to_work_rate=self.infection_too_sick_to_work_rate, + population_between_age_18_65=self.pop_between_age_18_65, + ) + self.maximum_productivity_t = max_productivity_t + + # Economic reward non-linearity + self.economic_reward_crra_eta = self.np_float_dtype(economic_reward_crra_eta) + assert 0.0 <= self.economic_reward_crra_eta < 20.0 + + # Health indices are normalized by maximum annual GDP + self.agents_health_norm = self.maximum_productivity_t * self.num_days_in_an_year + self.planner_health_norm = np.sum(self.agents_health_norm) + + # Economic indices are normalized by maximum annual GDP + self.agents_economic_norm = ( + self.maximum_productivity_t * self.num_days_in_an_year + ) + self.planner_economic_norm = np.sum(self.agents_economic_norm) + + def scale_health_over_economic_index(health_priority_scaling, alphas): + """ + Given starting alpha(s), compute new alphas so that the + resulting alpha:1-alpha ratio is scaled by health_weightage + """ + z = alphas / (1 - alphas) # alphas = z / (1 + z) + scaled_z = health_priority_scaling * z + new_alphas = scaled_z / (1 + scaled_z) + return new_alphas + + # Agents' health and economic index weightages + # fmt: off + self.weightage_on_marginal_agent_health_index = \ + scale_health_over_economic_index( + health_priority_scaling_agents, + self.inferred_weightage_on_agent_health_index, + ) + # fmt: on + assert ( + (self.weightage_on_marginal_agent_health_index >= 0) + & (self.weightage_on_marginal_agent_health_index <= 1) + ).all() + self.weightage_on_marginal_agent_economic_index = ( + 1 - self.weightage_on_marginal_agent_health_index + ) + + # Planner's health and economic index weightages + # fmt: off + self.weightage_on_marginal_planner_health_index = \ + scale_health_over_economic_index( + health_priority_scaling_planner, + self.inferred_weightage_on_planner_health_index, + ) + # fmt: on + assert 0 <= self.weightage_on_marginal_planner_health_index <= 1 + self.weightage_on_marginal_planner_economic_index = ( + 1 - self.weightage_on_marginal_planner_health_index + ) + + # Normalization factor for the reward (often useful for RL training) + self.reward_normalization_factor = reward_normalization_factor + + # CUDA-related attributes (for GPU simulations) + # Note: these will be set / overwritten via the env_wrapper + # use_cuda will be set to True (by the env_wrapper), if needed + # to be simulated on the GPU + self.use_cuda = False + self.cuda_data_manager = None + self.cuda_function_manager = None + self.cuda_step = lambda *args, **kwargs: None + self.cuda_compute_reward = lambda *args, **kwargs: None + + # Adding use_cuda to self.world for use in components + self.world.use_cuda = self.use_cuda + self.world.cuda_data_manager = self.cuda_data_manager + self.world.cuda_function_manager = self.cuda_function_manager + + name = "CovidAndEconomySimulation" + agent_subclasses = ["BasicMobileAgent", "BasicPlanner"] + + required_entities = [] + + def reset_starting_layout(self): + pass + + def reset_agent_states(self): + self.world.clear_agent_locs() + + def get_data_dictionary(self): + """ + Create a dictionary of data to push to the GPU (device). + """ + data_dict = DataFeed() + # Global States + data_dict.add_data( + name="susceptible", + data=self.world.global_state["Susceptible"], + save_copy_and_apply_at_reset=True, + ) + data_dict.add_data( + name="infected", + data=self.world.global_state["Infected"], + save_copy_and_apply_at_reset=True, + ) + data_dict.add_data( + name="recovered", + data=self.world.global_state["Recovered"], + save_copy_and_apply_at_reset=True, + ) + data_dict.add_data( + name="deaths", + data=self.world.global_state["Deaths"], + save_copy_and_apply_at_reset=True, + ) + data_dict.add_data( + name="unemployed", + data=self.world.global_state["Unemployed"], + save_copy_and_apply_at_reset=True, + ) + data_dict.add_data( + name="vaccinated", + data=self.world.global_state["Vaccinated"], + save_copy_and_apply_at_reset=True, + ) + # Actions + data_dict.add_data( + name="stringency_level", + data=self.world.global_state["Stringency Level"].astype(self.np_int_dtype), + save_copy_and_apply_at_reset=True, + ) + data_dict.add_data( + name="subsidy_level", + data=self.world.global_state["Subsidy Level"].astype(self.np_int_dtype), + save_copy_and_apply_at_reset=True, + ) + # Economy-related + data_dict.add_data( + name="subsidy", + data=self.world.global_state["Subsidy"], + save_copy_and_apply_at_reset=True, + ) + data_dict.add_data( + name="postsubsidy_productivity", + data=self.world.global_state["Postsubsidy Productivity"], + save_copy_and_apply_at_reset=True, + ) + data_dict.add_data( + name="productivity", + data=np.zeros_like( + self.world.global_state["Susceptible"], dtype=self.np_float_dtype + ), + save_copy_and_apply_at_reset=True, + ) + data_dict.add_data( + name="incapacitated", + data=np.zeros((self.num_us_states), dtype=self.np_float_dtype), + save_copy_and_apply_at_reset=True, + ) + data_dict.add_data( + name="cant_work", + data=np.zeros((self.num_us_states), dtype=self.np_float_dtype), + save_copy_and_apply_at_reset=True, + ) + data_dict.add_data( + name="num_people_that_can_work", + data=np.zeros((self.num_us_states), dtype=self.np_float_dtype), + save_copy_and_apply_at_reset=True, + ) + data_dict.add_data( + name="us_state_population", + data=self.us_state_population, + ) + data_dict.add_data( + name="infection_too_sick_to_work_rate", + data=self.infection_too_sick_to_work_rate, + ) + data_dict.add_data( + name="population_between_age_18_65", + data=self.pop_between_age_18_65, + ) + data_dict.add_data( + name="daily_production_per_worker", + data=self.daily_production_per_worker, + ) + data_dict.add_data( + name="maximum_productivity", + data=self.maximum_productivity_t, + ) + # SIR-related + data_dict.add_data( + name="real_world_stringency_policy_history", + data=( + self._real_world_data["policy"][ + self.start_date_index - self.beta_delay + 1 : self.start_date_index, + :, + ] + ).astype(self.np_int_dtype), + ) + data_dict.add_data( + name="beta_delay", + data=self.beta_delay, + ) + data_dict.add_data( + name="beta_slopes", + data=self.beta_slopes, + ) + data_dict.add_data( + name="beta_intercepts", + data=self.beta_intercepts, + ) + data_dict.add_data( + name="beta", + data=np.zeros((self.num_us_states), dtype=self.np_float_dtype), + save_copy_and_apply_at_reset=True, + ) + data_dict.add_data( + name="gamma", + data=self.gamma, + ) + data_dict.add_data( + name="death_rate", + data=self.death_rate, + ) + # Unemployment fit parameters + data_dict.add_data( + name="filter_len", + data=self.filter_len, + ) + data_dict.add_data( + name="num_filters", + data=self.num_filters, + ) + data_dict.add_data( + name="delta_stringency_level", + data=( + self.stringency_level_history[1:] - self.stringency_level_history[:-1] + ).astype(self.np_int_dtype), + save_copy_and_apply_at_reset=True, + ) + data_dict.add_data( + name="grouped_convolutional_filter_weights", + data=self.grouped_convolutional_filter_weights, + ) + data_dict.add_data( + name="unemp_conv_filters", + data=self.unemp_conv_filters, + ) + data_dict.add_data( + name="unemployment_bias", + data=self.unemployment_bias, + ) + data_dict.add_data( + name="signal", + data=np.zeros( + (self.n_agents, self.num_filters, self.filter_len), + dtype=self.np_float_dtype, + ), + save_copy_and_apply_at_reset=True, + ) + # Reward-related + data_dict.add_data( + name="min_marginal_agent_health_index", + data=self.min_marginal_agent_health_index, + ) + data_dict.add_data( + name="max_marginal_agent_health_index", + data=self.max_marginal_agent_health_index, + ) + data_dict.add_data( + name="min_marginal_agent_economic_index", + data=self.min_marginal_agent_economic_index, + ) + data_dict.add_data( + name="max_marginal_agent_economic_index", + data=self.max_marginal_agent_economic_index, + ) + data_dict.add_data( + name="min_marginal_planner_health_index", + data=self.min_marginal_planner_health_index, + ) + data_dict.add_data( + name="max_marginal_planner_health_index", + data=self.max_marginal_planner_health_index, + ) + data_dict.add_data( + name="min_marginal_planner_economic_index", + data=self.min_marginal_planner_economic_index, + ) + data_dict.add_data( + name="max_marginal_planner_economic_index", + data=self.max_marginal_planner_economic_index, + ) + data_dict.add_data( + name="weightage_on_marginal_agent_health_index", + data=self.weightage_on_marginal_agent_health_index, + ) + data_dict.add_data( + name="weightage_on_marginal_agent_economic_index", + data=self.weightage_on_marginal_agent_economic_index, + ) + data_dict.add_data( + name="weightage_on_marginal_planner_health_index", + data=self.weightage_on_marginal_planner_health_index, + ) + data_dict.add_data( + name="weightage_on_marginal_planner_economic_index", + data=self.weightage_on_marginal_planner_economic_index, + ) + data_dict.add_data( + name="value_of_life", + data=self.value_of_life, + ) + data_dict.add_data( + name="economic_reward_crra_eta", + data=self.economic_reward_crra_eta, + ) + data_dict.add_data( + name="num_days_in_an_year", + data=self.num_days_in_an_year, + ) + data_dict.add_data( + name="risk_free_interest_rate", + data=self.risk_free_interest_rate, + ) + data_dict.add_data( + name="agents_health_norm", + data=self.agents_health_norm, + ) + data_dict.add_data( + name="agents_economic_norm", + data=self.agents_economic_norm, + ) + data_dict.add_data( + name="planner_health_norm", + data=self.planner_health_norm, + ) + data_dict.add_data( + name="planner_economic_norm", + data=self.planner_economic_norm, + ) + + return data_dict + + def get_tensor_dictionary(self): + """ + Create a dictionary of (Pytorch-accessible) data to push to the GPU (device). + """ + tensor_dict = DataFeed() + return tensor_dict + + def scenario_step(self): + """ + Update the state of the USA based on the Covid-19 and Economy dynamics. + This internally implements three steps + - sir_step() - updates the susceptible, infected, recovered, deaths + and vaccination numbers based on the SIR equations + - unemployment_step() - uses the unemployment model to updates the unemployment + based on the stringency levels + - economy_step - computes the current producitivity numbers for the agents + """ + if self.use_cuda: + self.cuda_step( + self.cuda_data_manager.device_data("susceptible"), + self.cuda_data_manager.device_data("infected"), + self.cuda_data_manager.device_data("recovered"), + self.cuda_data_manager.device_data("deaths"), + self.cuda_data_manager.device_data("vaccinated"), + self.cuda_data_manager.device_data("unemployed"), + self.cuda_data_manager.device_data("subsidy"), + self.cuda_data_manager.device_data("productivity"), + self.cuda_data_manager.device_data("stringency_level"), + self.cuda_data_manager.device_data("num_stringency_levels"), + self.cuda_data_manager.device_data("postsubsidy_productivity"), + self.cuda_data_manager.device_data("num_vaccines_available_t"), + self.cuda_data_manager.device_data( + "real_world_stringency_policy_history" + ), + self.cuda_data_manager.device_data("beta_delay"), + self.cuda_data_manager.device_data("beta_slopes"), + self.cuda_data_manager.device_data("beta_intercepts"), + self.cuda_data_manager.device_data("beta"), + self.cuda_data_manager.device_data("gamma"), + self.cuda_data_manager.device_data("death_rate"), + self.cuda_data_manager.device_data("incapacitated"), + self.cuda_data_manager.device_data("cant_work"), + self.cuda_data_manager.device_data("num_people_that_can_work"), + self.cuda_data_manager.device_data("us_state_population"), + self.cuda_data_manager.device_data("infection_too_sick_to_work_rate"), + self.cuda_data_manager.device_data("population_between_age_18_65"), + self.cuda_data_manager.device_data("filter_len"), + self.cuda_data_manager.device_data("num_filters"), + self.cuda_data_manager.device_data("delta_stringency_level"), + self.cuda_data_manager.device_data( + "grouped_convolutional_filter_weights" + ), + self.cuda_data_manager.device_data("unemp_conv_filters"), + self.cuda_data_manager.device_data("unemployment_bias"), + self.cuda_data_manager.device_data("signal"), + self.cuda_data_manager.device_data("daily_production_per_worker"), + self.cuda_data_manager.device_data("maximum_productivity"), + self.cuda_data_manager.device_data( + f"{_OBSERVATIONS}_a_world-agent_state" + ), + self.cuda_data_manager.device_data( + f"{_OBSERVATIONS}_a_world-agent_postsubsidy_productivity" + ), + self.cuda_data_manager.device_data( + f"{_OBSERVATIONS}_a_world-lagged_stringency_level" + ), + self.cuda_data_manager.device_data(f"{_OBSERVATIONS}_a_time"), + self.cuda_data_manager.device_data( + f"{_OBSERVATIONS}_p_world-agent_state" + ), + self.cuda_data_manager.device_data( + f"{_OBSERVATIONS}_p_world-agent_postsubsidy_productivity" + ), + self.cuda_data_manager.device_data( + f"{_OBSERVATIONS}_p_world-lagged_stringency_level" + ), + self.cuda_data_manager.device_data(f"{_OBSERVATIONS}_p_time"), + self.cuda_data_manager.device_data("_timestep_"), + self.cuda_data_manager.meta_info("n_agents"), + self.cuda_data_manager.meta_info("episode_length"), + block=self.world.cuda_function_manager.block, + grid=self.world.cuda_function_manager.grid, + ) + else: + prev_t = self.world.timestep - 1 + curr_t = self.world.timestep + + self.current_date += timedelta(days=1) + + # SIR + # --- + if self.use_real_world_data: + _S_t = np.maximum( + self._real_world_data["susceptible"][ + curr_t + self.start_date_index + ], + 0, + ) + _I_t = np.maximum( + self._real_world_data["infected"][curr_t + self.start_date_index], + 0, + ) + _R_t = np.maximum( + self._real_world_data["recovered"][curr_t + self.start_date_index], + 0, + ) + _V_t = np.maximum( + self._real_world_data["vaccinated"][curr_t + self.start_date_index], + 0, + ) + _D_t = np.maximum( + self._real_world_data["deaths"][curr_t + self.start_date_index], + 0, + ) + + else: # Use simulation logic + if curr_t - self.beta_delay < 0: + if self.start_date_index + curr_t - self.beta_delay < 0: + stringency_level_tmk = np.ones(self.num_us_states) + else: + stringency_level_tmk = self._real_world_data["policy"][ + self.start_date_index + curr_t - self.beta_delay, : + ] + else: + stringency_level_tmk = self.world.global_state["Stringency Level"][ + curr_t - self.beta_delay + ] + stringency_level_tmk = stringency_level_tmk.astype(self.np_int_dtype) + + _S_tm1 = self.world.global_state["Susceptible"][prev_t] + _I_tm1 = self.world.global_state["Infected"][prev_t] + _R_tm1 = self.world.global_state["Recovered"][prev_t] + _V_tm1 = self.world.global_state["Vaccinated"][prev_t] + + # Vaccination + # ----------- + num_vaccines_available_t = np.zeros( + self.n_agents, dtype=self.np_int_dtype + ) + for aidx, agent in enumerate(self.world.agents): + # "Load" the vaccines in the inventory into this vector. + num_vaccines_available_t[aidx] = agent.state["Vaccines Available"] + # Agents always use whatever vaccines they can, so this becomes 0: + agent.state["Total Vaccinated"] += agent.state["Vaccines Available"] + agent.state["Vaccines Available"] = 0 + + # SIR step + # -------- + _dS, _dI, _dR, _dV = self.sir_step( + _S_tm1, + _I_tm1, + stringency_level_tmk, + num_vaccines_available_t, + ) + _S_t = np.maximum(_S_tm1 + _dS, 0) + _I_t = np.maximum(_I_tm1 + _dI, 0) + _R_t = np.maximum(_R_tm1 + _dR, 0) + _V_t = np.maximum(_V_tm1 + _dV, 0) + + num_recovered_but_not_vaccinated_t = _R_t - _V_t + _D_t = self.death_rate * num_recovered_but_not_vaccinated_t + + # Update global state + # ------------------- + self.world.global_state["Susceptible"][curr_t] = _S_t + self.world.global_state["Infected"][curr_t] = _I_t + self.world.global_state["Recovered"][curr_t] = _R_t + self.world.global_state["Deaths"][curr_t] = _D_t + self.world.global_state["Vaccinated"][curr_t] = _V_t + + # Unemployment + # ------------ + if self.use_real_world_data: + num_unemployed_t = self._real_world_data["unemployed"][ + self.start_date_index + curr_t + ] + else: + num_unemployed_t = self.unemployment_step( + current_stringency_level=self.world.global_state[ + "Stringency Level" + ][curr_t] + ) + + self.world.global_state["Unemployed"][curr_t] = num_unemployed_t + + # Productivity + # ------------ + productivity_t = self.economy_step( + self.us_state_population, + infected=_I_t, + deaths=_D_t, + unemployed=num_unemployed_t, + infection_too_sick_to_work_rate=self.infection_too_sick_to_work_rate, + population_between_age_18_65=self.pop_between_age_18_65, + ) + + # Subsidies + # --------- + # Add federal government subsidy to productivity + daily_statewise_subsidy_t = self.world.global_state["Subsidy"][curr_t] + postsubsidy_productivity_t = productivity_t + daily_statewise_subsidy_t + self.world.global_state["Postsubsidy Productivity"][ + curr_t + ] = postsubsidy_productivity_t + + # Update agent state + # ------------------ + current_date_string = datetime.strftime( + self.current_date, format=self.date_format + ) + for agent in self.world.agents: + agent.state["Total Susceptible"] = _S_t[agent.idx].astype( + self.np_int_dtype + ) + agent.state["New Infections"] = ( + _I_t[agent.idx] - agent.state["Total Infected"] + ).astype(self.np_int_dtype) + agent.state["Total Infected"] = _I_t[agent.idx].astype( + self.np_int_dtype + ) + agent.state["Total Recovered"] = _R_t[agent.idx].astype( + self.np_int_dtype + ) + agent.state["New Deaths"] = _D_t[agent.idx] - agent.state[ + "Total Deaths" + ].astype(self.np_int_dtype) + agent.state["Total Deaths"] = _D_t[agent.idx].astype(self.np_int_dtype) + agent.state["Total Vaccinated"] = _V_t[agent.idx].astype( + self.np_int_dtype + ) + + agent.state["Total Unemployed"] = num_unemployed_t[agent.idx].astype( + self.np_int_dtype + ) + agent.state["New Subsidy Received"] = daily_statewise_subsidy_t[ + agent.idx + ] + agent.state["Postsubsidy Productivity"] = postsubsidy_productivity_t[ + agent.idx + ] + agent.state["Date"] = current_date_string + + # Update planner state + # -------------------- + self.world.planner.state["Total Susceptible"] = np.sum(_S_t).astype( + self.np_int_dtype + ) + self.world.planner.state["New Infections"] = ( + np.sum(_I_t) - self.world.planner.state["Total Infected"] + ).astype(self.np_int_dtype) + self.world.planner.state["Total Infected"] = np.sum(_I_t).astype( + self.np_int_dtype + ) + self.world.planner.state["Total Recovered"] = np.sum(_R_t).astype( + self.np_int_dtype + ) + self.world.planner.state["New Deaths"] = ( + np.sum(_D_t) - self.world.planner.state["Total Deaths"] + ).astype(self.np_int_dtype) + self.world.planner.state["Total Deaths"] = np.sum(_D_t).astype( + self.np_int_dtype + ) + self.world.planner.state["Total Vaccinated"] = np.sum(_V_t).astype( + self.np_int_dtype + ) + self.world.planner.state["Total Unemployed"] = np.sum( + num_unemployed_t + ).astype(self.np_int_dtype) + self.world.planner.state["New Subsidy Provided"] = np.sum( + daily_statewise_subsidy_t + ) + self.world.planner.state["Postsubsidy Productivity"] = np.sum( + postsubsidy_productivity_t + ) + self.world.planner.state["Date"] = current_date_string + + def generate_observations(self): + """ + - Process agent-specific and planner-specific data into an observation. + - Observations contain only the relevant features for that actor. + :return: a dictionary of observations for each agent and planner + """ + redux_agent_global_state = None + for feature in [ + "Susceptible", + "Infected", + "Recovered", + "Deaths", + "Vaccinated", + "Unemployed", + ]: + if redux_agent_global_state is None: + redux_agent_global_state = self.world.global_state[feature][ + self.world.timestep + ] + else: + redux_agent_global_state = np.vstack( + ( + redux_agent_global_state, + self.world.global_state[feature][self.world.timestep], + ) + ) + normalized_redux_agent_state = ( + redux_agent_global_state / self.us_state_population[None] + ) + + # Productivity + postsubsidy_productivity_t = self.world.global_state[ + "Postsubsidy Productivity" + ][self.world.timestep] + normalized_postsubsidy_productivity_t = ( + postsubsidy_productivity_t / self.maximum_productivity_t + ) + + # Let agents know about the policy about to affect SIR infection-rate beta + t_beta = self.world.timestep - self.beta_delay + 1 + if t_beta < 0: + lagged_stringency_level = self._real_world_data["policy"][ + self.start_date_index + t_beta + ] + else: + lagged_stringency_level = self.world.global_state["Stringency Level"][ + t_beta + ] + + normalized_lagged_stringency_level = ( + lagged_stringency_level / self.num_stringency_levels + ) + + # To condition policy on agent id + agent_index = np.eye(self.n_agents, dtype=self.np_int_dtype) + + # Observation dict - Agents + # ------------------------- + obs_dict = dict() + obs_dict["a"] = { + "agent_index": agent_index, + "agent_state": normalized_redux_agent_state, + "agent_postsubsidy_productivity": normalized_postsubsidy_productivity_t, + "lagged_stringency_level": normalized_lagged_stringency_level, + } + + # Observation dict - Planner + # -------------------------- + obs_dict[self.world.planner.idx] = { + "agent_state": normalized_redux_agent_state, + "agent_postsubsidy_productivity": normalized_postsubsidy_productivity_t, + "lagged_stringency_level": normalized_lagged_stringency_level, + } + + return obs_dict + + def compute_reward(self): + """ + Compute the social welfare metrics for each agent and the planner. + :return: a dictionary of rewards for each agent in the simulation + """ + if self.use_cuda: + self.cuda_compute_reward( + self.cuda_data_manager.device_data(f"{_REWARDS}_a"), + self.cuda_data_manager.device_data(f"{_REWARDS}_p"), + self.cuda_data_manager.device_data("num_days_in_an_year"), + self.cuda_data_manager.device_data("value_of_life"), + self.cuda_data_manager.device_data("risk_free_interest_rate"), + self.cuda_data_manager.device_data("economic_reward_crra_eta"), + self.cuda_data_manager.device_data("min_marginal_agent_health_index"), + self.cuda_data_manager.device_data("max_marginal_agent_health_index"), + self.cuda_data_manager.device_data("min_marginal_agent_economic_index"), + self.cuda_data_manager.device_data("max_marginal_agent_economic_index"), + self.cuda_data_manager.device_data("min_marginal_planner_health_index"), + self.cuda_data_manager.device_data("max_marginal_planner_health_index"), + self.cuda_data_manager.device_data( + "min_marginal_planner_economic_index" + ), + self.cuda_data_manager.device_data( + "max_marginal_planner_economic_index" + ), + self.cuda_data_manager.device_data( + "weightage_on_marginal_agent_health_index" + ), + self.cuda_data_manager.device_data( + "weightage_on_marginal_agent_economic_index" + ), + self.cuda_data_manager.device_data( + "weightage_on_marginal_planner_health_index" + ), + self.cuda_data_manager.device_data( + "weightage_on_marginal_planner_economic_index" + ), + self.cuda_data_manager.device_data("agents_health_norm"), + self.cuda_data_manager.device_data("agents_economic_norm"), + self.cuda_data_manager.device_data("planner_health_norm"), + self.cuda_data_manager.device_data("planner_economic_norm"), + self.cuda_data_manager.device_data("deaths"), + self.cuda_data_manager.device_data("subsidy"), + self.cuda_data_manager.device_data("postsubsidy_productivity"), + self.cuda_data_manager.device_data("_done_"), + self.cuda_data_manager.device_data("_timestep_"), + self.cuda_data_manager.meta_info("n_agents"), + self.cuda_data_manager.meta_info("episode_length"), + block=self.world.cuda_function_manager.block, + grid=self.world.cuda_function_manager.grid, + ) + return {} # Return empty dict. Reward arrays are updated in-place + rew = {"a": 0, "p": 0} + + def crra_nonlinearity(x, eta): + # Reference: https://en.wikipedia.org/wiki/Isoelastic_utility + # To be applied to (marginal) economic indices + annual_x = self.num_days_in_an_year * x + annual_x_clipped = np.clip(annual_x, 0.1, 3) + annual_crra = 1 + (annual_x_clipped ** (1 - eta) - 1) / (1 - eta) + daily_crra = annual_crra / self.num_days_in_an_year + return daily_crra + + def min_max_normalization(x, min_x, max_x): + eps = 1e-10 + return (x - min_x) / (max_x - min_x + eps) + + def get_weighted_average( + health_index_weightage, + health_index, + economic_index_weightage, + economic_index, + ): + return ( + health_index_weightage * health_index + + economic_index_weightage * economic_index + ) / (health_index_weightage + economic_index_weightage) + + # Changes this last timestep: + marginal_deaths = ( + self.world.global_state["Deaths"][self.world.timestep] + - self.world.global_state["Deaths"][self.world.timestep - 1] + ) + + subsidy_t = self.world.global_state["Subsidy"][self.world.timestep] + postsubsidy_productivity_t = self.world.global_state[ + "Postsubsidy Productivity" + ][self.world.timestep] + + # Health index -- the cost equivalent (annual GDP) of covid deaths + # Note: casting deaths to float to prevent overflow issues + marginal_agent_health_index = ( + -marginal_deaths.astype(self.np_float_dtype) + * self.value_of_life + / self.agents_health_norm + ).astype(self.np_float_dtype) + + # Economic index -- fraction of annual GDP achieved + # Use a "crra" nonlinearity on the agent economic reward + marginal_agent_economic_index = crra_nonlinearity( + postsubsidy_productivity_t / self.agents_economic_norm, + self.economic_reward_crra_eta, + ).astype(self.np_float_dtype) + + # Min-max Normalization + marginal_agent_health_index = min_max_normalization( + marginal_agent_health_index, + self.min_marginal_agent_health_index, + self.max_marginal_agent_health_index, + ).astype(self.np_float_dtype) + marginal_agent_economic_index = min_max_normalization( + marginal_agent_economic_index, + self.min_marginal_agent_economic_index, + self.max_marginal_agent_economic_index, + ).astype(self.np_float_dtype) + + # Agent Rewards + # ------------- + agent_rewards = get_weighted_average( + self.weightage_on_marginal_agent_health_index, + marginal_agent_health_index, + self.weightage_on_marginal_agent_economic_index, + marginal_agent_economic_index, + ) + rew["a"] = agent_rewards / self.reward_normalization_factor + + # Update agent states + # ------------------- + for agent in self.world.agents: + agent.state["Health Index"] += marginal_agent_health_index[agent.idx] + agent.state["Economic Index"] += marginal_agent_economic_index[agent.idx] + + # National level + # -------------- + # Health index -- the cost equivalent (annual GDP) of covid deaths + # Note: casting deaths to float to prevent overflow issues + marginal_planner_health_index = ( + -np.sum(marginal_deaths).astype(self.np_float_dtype) + * self.value_of_life + / self.planner_health_norm + ) + + # Economic index -- fraction of annual GDP achieved (minus subsidy cost) + cost_of_subsidy_t = (1 + self.risk_free_interest_rate) * np.sum(subsidy_t) + # Use a "crra" nonlinearity on the planner economic reward + marginal_planner_economic_index = crra_nonlinearity( + (np.sum(postsubsidy_productivity_t) - cost_of_subsidy_t) + / self.planner_economic_norm, + self.economic_reward_crra_eta, + ) + + # Min-max Normalization + marginal_planner_health_index = min_max_normalization( + marginal_planner_health_index, + self.min_marginal_planner_health_index, + self.max_marginal_planner_health_index, + ) + marginal_planner_economic_index = min_max_normalization( + marginal_planner_economic_index, + self.min_marginal_planner_economic_index, + self.max_marginal_planner_economic_index, + ) + + # Update planner states + # ------------------- + self.world.planner.state["Health Index"] += marginal_planner_health_index + self.world.planner.state["Economic Index"] += marginal_planner_economic_index + + # Planner Reward + # -------------- + planner_rewards = get_weighted_average( + self.weightage_on_marginal_planner_health_index, + marginal_planner_health_index, + self.weightage_on_marginal_planner_economic_index, + marginal_planner_economic_index, + ) + rew[self.world.planner.idx] = planner_rewards / self.reward_normalization_factor + + return rew + + def additional_reset_steps(self): + assert self.world.timestep == 0 + + # Reset current date + self.current_date = self.start_date + + # SIR numbers at timestep 0 + susceptible_0 = self._real_world_data["susceptible"][self.start_date_index] + infected_0 = self._real_world_data["infected"][self.start_date_index] + newly_infected_0 = ( + infected_0 + - self._real_world_data["infected"][max(0, self.start_date_index - 1)] + ) + recovered_0 = self._real_world_data["recovered"][self.start_date_index] + deaths_0 = recovered_0 * self.death_rate + + # Unemployment and vaccinated numbers at timestep 0 + unemployed_0 = self._real_world_data["unemployed"][self.start_date_index] + vaccinated_0 = self._real_world_data["vaccinated"][self.start_date_index] + + # Create a global state dictionary to save episode data + self.world.global_state = {} + self.set_global_state("Susceptible", susceptible_0, t=self.world.timestep) + self.set_global_state("Infected", infected_0, t=self.world.timestep) + self.set_global_state("Recovered", recovered_0, t=self.world.timestep) + self.set_global_state("Deaths", deaths_0, t=self.world.timestep) + + self.set_global_state("Unemployed", unemployed_0, t=self.world.timestep) + self.set_global_state("Vaccinated", vaccinated_0, t=self.world.timestep) + + new_deaths_0 = ( + deaths_0 + - self._real_world_data["recovered"][max(0, self.start_date_index - 1)] + * self.death_rate + ) + + # Reset stringency level history. + # Pad with stringency levels of 1 corresponding to states being fully open + # (as was the case before the pandemic). + self.stringency_level_history = np.pad( + self._real_world_data["policy"][: self.start_date_index + 1], + [(self.filter_len, 0), (0, 0)], + constant_values=1, + )[-(self.filter_len + 1) :] + + # Set the stringency level based to the real-world policy + self.set_global_state( + "Stringency Level", + self._real_world_data["policy"][self.start_date_index], + t=self.world.timestep, + ) + + # All US states start with zero subsidy and zero Postsubsidy Productivity + self.set_global_state("Subsidy Level", dtype=self.np_float_dtype) + self.set_global_state("Subsidy", dtype=self.np_float_dtype) + self.set_global_state("Postsubsidy Productivity", dtype=self.np_float_dtype) + + # Set initial agent states + # ------------------------ + current_date_string = datetime.strftime( + self.current_date, format=self.date_format + ) + + for agent in self.world.agents: + agent.state["Total Susceptible"] = susceptible_0[agent.idx].astype( + self.np_int_dtype + ) + agent.state["New Infections"] = newly_infected_0[agent.idx].astype( + self.np_int_dtype + ) + agent.state["Total Infected"] = infected_0[agent.idx].astype( + self.np_int_dtype + ) + agent.state["Total Recovered"] = recovered_0[agent.idx].astype( + self.np_int_dtype + ) + agent.state["New Deaths"] = new_deaths_0[agent.idx].astype( + self.np_int_dtype + ) + agent.state["Total Deaths"] = deaths_0[agent.idx].astype(self.np_int_dtype) + agent.state["Health Index"] = np.array([0]).astype(self.np_float_dtype) + agent.state["Economic Index"] = np.array([0]).astype(self.np_float_dtype) + agent.state["Date"] = current_date_string + + # Planner state fields + self.world.planner.state["Total Susceptible"] = np.sum( + [agent.state["Total Susceptible"] for agent in self.world.agents] + ).astype(self.np_int_dtype) + self.world.planner.state["New Infections"] = np.sum( + [agent.state["New Infections"] for agent in self.world.agents] + ).astype(self.np_int_dtype) + self.world.planner.state["Total Infected"] = np.sum( + [agent.state["Total Infected"] for agent in self.world.agents] + ).astype(self.np_int_dtype) + self.world.planner.state["Total Recovered"] = np.sum( + [agent.state["Total Recovered"] for agent in self.world.agents] + ).astype(self.np_int_dtype) + self.world.planner.state["New Deaths"] = np.sum( + [agent.state["New Deaths"] for agent in self.world.agents] + ).astype(self.np_int_dtype) + self.world.planner.state["Total Deaths"] = np.sum( + [agent.state["Total Deaths"] for agent in self.world.agents] + ).astype(self.np_int_dtype) + self.world.planner.state["Total Vaccinated"] = np.sum(vaccinated_0).astype( + self.np_int_dtype + ) + self.world.planner.state["Health Index"] = np.array([0]).astype( + self.np_float_dtype + ) + self.world.planner.state["Economic Index"] = np.array([0]).astype( + self.np_float_dtype + ) + + self.world.planner.state["Date"] = current_date_string + + # Reset any manually set parameter modulations + self._beta_intercepts_modulation = 1 + self._beta_slopes_modulation = 1 + self._unemployment_modulation = 1 + + def set_global_state(self, key=None, value=None, t=None, dtype=None): + # Use floats by default for the SIR dynamics + if dtype is None: + dtype = self.np_float_dtype + assert key in [ + "Susceptible", + "Infected", + "Recovered", + "Deaths", + "Unemployed", + "Vaccinated", + "Stringency Level", + "Subsidy Level", + "Subsidy", + "Postsubsidy Productivity", + ] + # If no values are passed, set everything to zeros. + if key not in self.world.global_state: + self.world.global_state[key] = np.zeros( + (self.episode_length + 1, self.num_us_states), dtype=dtype + ) + + if t is not None and value is not None: + assert isinstance(value, np.ndarray) + assert value.shape[0] == self.world.global_state[key].shape[1] + + self.world.global_state[key][t] = value + else: + pass + + def set_parameter_modulations( + self, beta_intercept=None, beta_slope=None, unemployment=None + ): + """ + Apply parameter modulation, which will be in effect until the next env reset. + + Each modulation term scales the associated set of model parameters by the + input value. This method is useful for performing a sensitivity analysis. + + In effect, the transmission rate (beta) will be calculated as: + beta = (m_s * beta_slope)*lagged_stringency + (m_i * beta_intercept) + + The unemployment rate (u) will be calculated as: + u = SOFTPLUS( m_u * SUM(u_filter_weight * u_filter_response) ) + u_0 + + Args: + beta_intercept: (float, >= 0) Modulation applied to the intercept term + of the beta model, m_i in above equations + beta_slope: (float, >= 0) Modulation applied to the slope term of the + beta model, m_s in above equations + unemployment: (float, >= 0) Modulation applied to the weighted sum of + unemployment filter responses, m_u in above equations. + + Example: + # Reset the environment + env.reset() + + # Increase the slope of the beta response by 15% + env.set_parameter_modulations(beta_slope=1.15) + + # Run the environment (this example skips over action selection for brevity) + for t in range(env.episode_length): + env.step(actions[t]) + """ + if beta_intercept is not None: + beta_intercept = float(beta_intercept) + assert beta_intercept >= 0 + self._beta_intercepts_modulation = beta_intercept + + if beta_slope is not None: + beta_slope = float(beta_slope) + assert beta_slope >= 0 + self._beta_slopes_modulation = beta_slope + + if unemployment is not None: + unemployment = float(unemployment) + assert unemployment >= 0 + self._unemployment_modulation = unemployment + + def unemployment_step(self, current_stringency_level): + """ + Computes unemployment given the current stringency level and past levels. + + Unemployment is computed as follows: + 1) For each of self.num_filters, an exponentially decaying filter is + convolved with the history of stringency changes. Responses move forward in + time, so a stringency change at time t-1 impacts the response at time t. + 2) The filter responses at time t (the current timestep) are summed together + using state-specific weights. + 3) The weighted sum is passed through a SOFTPLUS function to capture excess + unemployment due to stringency policy. + 4) The excess unemployment is added to a state-specific baseline unemployment + level to get the total unemployment. + + Note: Internally, unemployment is computed somewhat differently for speed. + In particular, no convolution is used. Instead the "filter response" at + time t is just a temporally discounted sum of past stringency changes, + with the discounting given by the filter decay rate. + """ + + def softplus(x, beta=1, threshold=20): + """ + Numpy implementation of softplus. For reference, see + https://pytorch.org/docs/stable/generated/torch.nn.Softplus.html + """ + return 1 / beta * np.log(1 + np.exp(beta * x)) * ( + beta * x <= threshold + ) + x * (beta * x > threshold) + + if ( + self.world.timestep == 0 + ): # computing unemployment at closure policy "all ones" + delta_stringency_level = np.zeros((self.filter_len, self.num_us_states)) + else: + self.stringency_level_history = np.concatenate( + ( + self.stringency_level_history[1:], + current_stringency_level.reshape(1, -1), + ) + ) + delta_stringency_level = ( + self.stringency_level_history[1:] - self.stringency_level_history[:-1] + ) + + # Rather than modulating the unemployment params, + # modulate the deltas (same effect) + delta_stringency_level = delta_stringency_level * self._unemployment_modulation + + # Expand the [time, state] delta history to have a dimension for filter channel + x_data = delta_stringency_level[None].transpose(2, 0, 1) + + # Apply the state-specific filter weights to each channel + weighted_x_data = x_data * self.repeated_conv_weights + + # Compute the discounted sum of the weighted deltas, with each channel using + # a discounting rate reflecting the time constant of the filter channel. Also + # sum over channels and use a softplus to get excess unemployment. + excess_unemployment = softplus( + np.sum(weighted_x_data * self.unemp_conv_filters, axis=(1, 2)), beta=1 + ) + + # Add excess unemployment to baseline unemployment + unemployment_rate = excess_unemployment + self.unemployment_bias + + # Convert the rate (which is a percent) to raw numbers for output + num_unemployed_t = unemployment_rate * self.us_state_population / 100 + return num_unemployed_t + + # --- Scenario-specific --- + def economy_step( + self, + population, + infected, + deaths, + unemployed, + infection_too_sick_to_work_rate=0.05, + population_between_age_18_65=0.67, + ): + """ + Computes how much production occurs. + + Assumptions: + + - People that cannot work: "infected + aware" and "unemployed" and "deaths". + - No life/death cycles. + + See __init__() for pre-computation of each worker's daily productivity. + """ + + incapacitated = (infection_too_sick_to_work_rate * infected) + deaths + cant_work = (incapacitated * population_between_age_18_65) + unemployed + + num_workers = population * population_between_age_18_65 + + num_people_that_can_work = np.maximum(0, num_workers - cant_work) + + productivity = ( + num_people_that_can_work * self.daily_production_per_worker + ).astype(self.np_float_dtype) + + return productivity + + def sir_step(self, S_tm1, I_tm1, stringency_level_tmk, num_vaccines_available_t): + """ + Simulates SIR infection model in the US. + """ + intercepts = self.beta_intercepts * self._beta_intercepts_modulation + slopes = self.beta_slopes * self._beta_slopes_modulation + beta_i = (intercepts + slopes * stringency_level_tmk).astype( + self.np_float_dtype + ) + + small_number = 1e-10 # used to prevent indeterminate cases + susceptible_fraction_vaccinated = np.minimum( + np.ones((self.num_us_states), dtype=self.np_int_dtype), + num_vaccines_available_t / (S_tm1 + small_number), + ).astype(self.np_float_dtype) + vaccinated_t = np.minimum(num_vaccines_available_t, S_tm1) + + # Record R0 + R0 = beta_i / self.gamma + for agent in self.world.agents: + agent.state["R0"] = R0[agent.idx] + + # S -> I; dS + neighborhood_SI_over_N = (S_tm1 / self.us_state_population) * I_tm1 + dS_t = ( + -beta_i * neighborhood_SI_over_N * (1 - susceptible_fraction_vaccinated) + - vaccinated_t + ).astype(self.np_float_dtype) + + # I -> R; dR + dR_t = (self.gamma * I_tm1 + vaccinated_t).astype(self.np_float_dtype) + + # dI from d(S + I + R) = 0 + # ------------------------ + dI_t = -dS_t - dR_t + + dV_t = vaccinated_t.astype(self.np_float_dtype) + + return dS_t, dI_t, dR_t, dV_t + + def load_model_constants(self, path_to_model_constants): + filename = "model_constants.json" + assert filename in os.listdir(path_to_model_constants), ( + "Unable to locate '{}' in '{}'.\nPlease run the " + "'gather_real_world_data.ipynb' notebook first".format( + filename, path_to_model_constants + ) + ) + with open(os.path.join(path_to_model_constants, filename), "r") as fp: + model_constants_dict = json.load(fp) + fp.close() + + self.date_format = model_constants_dict["DATE_FORMAT"] + self.us_state_idx_to_state_name = model_constants_dict[ + "US_STATE_IDX_TO_STATE_NAME" + ] + self.us_state_population = self.np_int_dtype( + model_constants_dict["US_STATE_POPULATION"] + ) + self.us_population = self.np_int_dtype(model_constants_dict["US_POPULATION"]) + self.num_stringency_levels = model_constants_dict["NUM_STRINGENCY_LEVELS"] + self.death_rate = self.np_float_dtype(model_constants_dict["SIR_MORTALITY"]) + self.gamma = self.np_float_dtype(model_constants_dict["SIR_GAMMA"]) + self.gdp_per_capita = self.np_float_dtype( + model_constants_dict["GDP_PER_CAPITA"] + ) + + def load_fitted_params(self, path_to_fitted_params): + filename = "fitted_params.json" + assert filename in os.listdir(path_to_fitted_params), ( + "Unable to locate '{}' in '{}'.\nIf you ran the " + "'gather_real_world_data.ipynb' notebook to download the latest " + "real-world data, please also run the " + "'fit_parameters.ipynb' notebook.".format(filename, path_to_fitted_params) + ) + with open(os.path.join(path_to_fitted_params, filename), "r") as fp: + fitted_params_dict = json.load(fp) + fp.close() + self.policy_start_date = datetime.strptime( + fitted_params_dict["POLICY_START_DATE"], self.date_format + ) + self.value_of_life = self.np_int_dtype(fitted_params_dict["VALUE_OF_LIFE"]) + self.beta_delay = self.np_int_dtype(fitted_params_dict["BETA_DELAY"]) + self.beta_slopes = np.array( + fitted_params_dict["BETA_SLOPES"], dtype=self.np_float_dtype + ) + self.beta_intercepts = np.array( + fitted_params_dict["BETA_INTERCEPTS"], dtype=self.np_float_dtype + ) + self.min_marginal_agent_health_index = np.array( + fitted_params_dict["MIN_MARGINAL_AGENT_HEALTH_INDEX"], + dtype=self.np_float_dtype, + ) + self.max_marginal_agent_health_index = np.array( + fitted_params_dict["MAX_MARGINAL_AGENT_HEALTH_INDEX"], + dtype=self.np_float_dtype, + ) + self.min_marginal_agent_economic_index = np.array( + fitted_params_dict["MIN_MARGINAL_AGENT_ECONOMIC_INDEX"], + dtype=self.np_float_dtype, + ) + self.max_marginal_agent_economic_index = np.array( + fitted_params_dict["MAX_MARGINAL_AGENT_ECONOMIC_INDEX"], + dtype=self.np_float_dtype, + ) + self.min_marginal_planner_health_index = self.np_float_dtype( + fitted_params_dict["MIN_MARGINAL_PLANNER_HEALTH_INDEX"] + ) + self.max_marginal_planner_health_index = self.np_float_dtype( + fitted_params_dict["MAX_MARGINAL_PLANNER_HEALTH_INDEX"] + ) + self.min_marginal_planner_economic_index = self.np_float_dtype( + fitted_params_dict["MIN_MARGINAL_PLANNER_ECONOMIC_INDEX"] + ) + self.max_marginal_planner_economic_index = self.np_float_dtype( + fitted_params_dict["MAX_MARGINAL_PLANNER_ECONOMIC_INDEX"] + ) + self.inferred_weightage_on_agent_health_index = np.array( + fitted_params_dict["INFERRED_WEIGHTAGE_ON_AGENT_HEALTH_INDEX"], + dtype=self.np_float_dtype, + ) + self.inferred_weightage_on_planner_health_index = self.np_float_dtype( + fitted_params_dict["INFERRED_WEIGHTAGE_ON_PLANNER_HEALTH_INDEX"] + ) + self.filter_len = self.np_int_dtype(fitted_params_dict["FILTER_LEN"]) + self.conv_lambdas = np.array( + fitted_params_dict["CONV_LAMBDAS"], dtype=self.np_float_dtype + ) + self.unemployment_bias = np.array( + fitted_params_dict["UNEMPLOYMENT_BIAS"], dtype=self.np_float_dtype + ) + self.grouped_convolutional_filter_weights = np.array( + fitted_params_dict["GROUPED_CONVOLUTIONAL_FILTER_WEIGHTS"], + dtype=self.np_float_dtype, + ) + + def scenario_metrics(self): + # End of episode metrics + # ---------------------- + metrics_dict = {} + + # State-level metrics + for agent in self.world.agents: + state_name = self.us_state_idx_to_state_name[str(agent.idx)] + + for field in ["infected", "recovered", "deaths"]: + metric_key = "{}/{} (millions)".format(state_name, field) + metrics_dict[metric_key] = ( + agent.state["Total " + field.capitalize()] / 1e6 + ) + + metrics_dict["{}/mean_unemployment_rate (%)".format(state_name)] = ( + np.mean(self.world.global_state["Unemployed"][1:, agent.idx], axis=0) + / self.us_state_population[agent.idx] + * 100 + ) + + metrics_dict[ + "{}/mean_open_close_stringency_level".format(state_name) + ] = np.mean( + self.world.global_state["Stringency Level"][1:, agent.idx], axis=0 + ) + + metrics_dict["{}/total_productivity (billion $)".format(state_name)] = ( + np.sum( + self.world.global_state["Postsubsidy Productivity"][1:, agent.idx] + ) + / 1e9 + ) + + metrics_dict[ + "{}/health_index_at_end_of_episode".format(state_name) + ] = agent.state["Health Index"] + metrics_dict[ + "{}/economic_index_at_end_of_episode".format(state_name) + ] = agent.state["Economic Index"] + + # USA-level metrics + metrics_dict["usa/vaccinated (% of population)"] = ( + np.sum(self.world.global_state["Vaccinated"][self.world.timestep], axis=0) + / self.us_population + * 100 + ) + metrics_dict["usa/deaths (thousands)"] = ( + np.sum(self.world.global_state["Deaths"][self.world.timestep], axis=0) / 1e3 + ) + + metrics_dict["usa/mean_unemployment_rate (%)"] = ( + np.mean( + np.sum(self.world.global_state["Unemployed"][1:], axis=1) + / self.us_population, + axis=0, + ) + * 100 + ) + metrics_dict["usa/total_amount_subsidized (trillion $)"] = ( + np.sum(self.world.global_state["Subsidy"][1:], axis=(0, 1)) / 1e12 + ) + metrics_dict["usa/total_productivity (trillion $)"] = ( + np.sum(self.world.global_state["Postsubsidy Productivity"][1:], axis=(0, 1)) + / 1e12 + ) + + metrics_dict["usa/health_index_at_end_of_episode"] = self.world.planner.state[ + "Health Index" + ] + metrics_dict["usa/economic_index_at_end_of_episode"] = self.world.planner.state[ + "Economic Index" + ] + + return metrics_dict diff --git a/ai_economist/foundation/scenarios/covid19/covid19_env_step.cu b/ai_economist/foundation/scenarios/covid19/covid19_env_step.cu new file mode 100644 index 0000000..cee1260 --- /dev/null +++ b/ai_economist/foundation/scenarios/covid19/covid19_env_step.cu @@ -0,0 +1,620 @@ +// Copyright (c) 2021, salesforce.com, inc. +// All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// For full license text, see the LICENSE file in the repo root +// or https://opensource.org/licenses/BSD-3-Clause + +__constant__ float kEpsilon = 1.0e-10; // used to prevent division by 0 + +extern "C" { +// CUDA version of the scenario_step() in +// "ai_economist.foundation.scenarios.covid19_env.py" + + // CUDA version of the sir_step() in + // "ai_economist.foundation.scenarios.covid19_env.py" + __device__ void cuda_sir_step( + float* susceptible, + float* infected, + float* recovered, + float* vaccinated, + float* deaths, + int* num_vaccines_available_t, + const int* kRealWorldStringencyPolicyHistory, + const float kStatePopulation, + const int kNumAgents, + const int kBetaDelay, + const float kBetaSlope, + const float kbetaIntercept, + int* stringency_level, + float* beta, + const float kGamma, + const float kDeathRate, + const int kEnvId, + const int kAgentId, + int timestep, + const int kEpisodeLength, + const int kArrayIdxCurrentTime, + const int kArrayIdxPrevTime, + const int kTimeIndependentArrayIdx + ) { + float susceptible_fraction_vaccinated = min( + 1.0, + num_vaccines_available_t[kTimeIndependentArrayIdx] / + (susceptible[kArrayIdxPrevTime] + kEpsilon)); + float vaccinated_t = min( + static_cast(num_vaccines_available_t[ + kTimeIndependentArrayIdx]), + susceptible[kArrayIdxPrevTime]); + + // (S/N) * I in place of (S*I) / N to prevent overflow + float neighborhood_SI_over_N = susceptible[kArrayIdxPrevTime] / + kStatePopulation * infected[kArrayIdxPrevTime]; + int stringency_level_tmk; + if (timestep < kBetaDelay) { + stringency_level_tmk = kRealWorldStringencyPolicyHistory[ + (timestep - 1) * (kNumAgents - 1) + kAgentId]; + } else { + stringency_level_tmk = stringency_level[kEnvId * ( + kEpisodeLength + 1) * (kNumAgents - 1) + + (timestep - kBetaDelay) * (kNumAgents - 1) + kAgentId]; + } + beta[kTimeIndependentArrayIdx] = stringency_level_tmk * + kBetaSlope + kbetaIntercept; + + float dS_t = -(neighborhood_SI_over_N * beta[ + kTimeIndependentArrayIdx] * + (1 - susceptible_fraction_vaccinated) + vaccinated_t); + float dR_t = kGamma * infected[kArrayIdxPrevTime] + vaccinated_t; + float dI_t = - dS_t - dR_t; + + susceptible[kArrayIdxCurrentTime] = max( + 0.0, + susceptible[kArrayIdxPrevTime] + dS_t); + infected[kArrayIdxCurrentTime] = max( + 0.0, + infected[kArrayIdxPrevTime] + dI_t); + recovered[kArrayIdxCurrentTime] = max( + 0.0, + recovered[kArrayIdxPrevTime] + dR_t); + + vaccinated[kArrayIdxCurrentTime] = vaccinated_t + + vaccinated[kArrayIdxPrevTime]; + float recovered_but_not_vaccinated = recovered[kArrayIdxCurrentTime] - + vaccinated[kArrayIdxCurrentTime]; + deaths[kArrayIdxCurrentTime] = recovered_but_not_vaccinated * + kDeathRate; + } + + // CUDA version of the softplus() in + // "ai_economist.foundation.scenarios.covid19_env.py" + __device__ float softplus(float x) { + const float kBeta = 1.0; + const float kThreshold = 20.0; + if (kBeta * x < kThreshold) { + return 1.0 / kBeta * log(1.0 + exp(kBeta * x)); + } else { + return x; + } + } + + __device__ float signal2unemployment( + const int kEnvId, + const int kAgentId, + float* signal, + const float* kUnemploymentConvolutionalFilters, + const float kUnemploymentBias, + const int kNumAgents, + const int kFilterLen, + const int kNumFilters + ) { + float unemployment = 0.0; + const int kArrayIndexOffset = kEnvId * (kNumAgents - 1) * kNumFilters * + kFilterLen + kAgentId * kNumFilters * kFilterLen; + for (int index = 0; index < (kFilterLen * kNumFilters); index ++) { + unemployment += signal[kArrayIndexOffset + index] * + kUnemploymentConvolutionalFilters[index]; + } + return softplus(unemployment) + kUnemploymentBias; + } + + // CUDA version of the unemployment_step() in + // "ai_economist.foundation.scenarios.covid19_env.py" + __device__ void cuda_unemployment_step( + float* unemployed, + int* stringency_level, + int* delta_stringency_level, + const float* kGroupedConvolutionalFilterWeights, + const float* kUnemploymentConvolutionalFilters, + const float* kUnemploymentBias, + float* convolved_signal, + const int kFilterLen, + const int kNumFilters, + const float kStatePopulation, + const int kNumAgents, + const int kEnvId, + const int kAgentId, + int timestep, + const int kArrayIdxCurrentTime, + const int kArrayIdxPrevTime + ) { + // Shift array by kNumAgents - 1 + for (int idx = 0; idx < kFilterLen - 1; idx ++) { + delta_stringency_level[ + kEnvId * kFilterLen * (kNumAgents - 1) + idx * + (kNumAgents - 1) + kAgentId + ] = + delta_stringency_level[ + kEnvId * kFilterLen * (kNumAgents - 1) + (idx + 1) * + (kNumAgents - 1) + kAgentId + ]; + } + + delta_stringency_level[ + kEnvId * kFilterLen * (kNumAgents - 1) + (kFilterLen - 1) * + (kNumAgents - 1) + kAgentId + ] = stringency_level[kArrayIdxCurrentTime] - + stringency_level[kArrayIdxPrevTime]; + + // convolved_signal refers to the convolution between the filter weights + // and the delta stringency levels + for (int filter_idx = 0; filter_idx < kNumFilters; filter_idx ++) { + for (int idx = 0; idx < kFilterLen; idx ++) { + convolved_signal[ + kEnvId * (kNumAgents - 1) * kNumFilters * kFilterLen + + kAgentId * kNumFilters * kFilterLen + + filter_idx * kFilterLen + + idx + ] = + delta_stringency_level[kEnvId * kFilterLen * (kNumAgents - 1) + + idx * (kNumAgents - 1) + kAgentId] * + kGroupedConvolutionalFilterWeights[kAgentId * kNumFilters + + filter_idx]; + } + } + + float unemployment_rate = signal2unemployment( + kEnvId, + kAgentId, + convolved_signal, + kUnemploymentConvolutionalFilters, + kUnemploymentBias[kAgentId], + kNumAgents, + kFilterLen, + kNumFilters); + + unemployed[kArrayIdxCurrentTime] = + unemployment_rate * kStatePopulation / 100.0; + } + + // CUDA version of the economy_step() in + // "ai_economist.foundation.scenarios.covid19_env.py" + __device__ void cuda_economy_step( + float* infected, + float* deaths, + float* unemployed, + float* incapacitated, + float* cant_work, + float* num_people_that_can_work, + const float kStatePopulation, + const float kInfectionTooSickToWorkRate, + const float kPopulationBetweenAge18And65, + const float kDailyProductionPerWorker, + float* productivity, + float* subsidy, + float* postsubsidy_productivity, + int timestep, + const int kArrayIdxCurrentTime, + int kTimeIndependentArrayIdx + ) { + incapacitated[kTimeIndependentArrayIdx] = + kInfectionTooSickToWorkRate * infected[kArrayIdxCurrentTime] + + deaths[kArrayIdxCurrentTime]; + cant_work[kTimeIndependentArrayIdx] = + incapacitated[kTimeIndependentArrayIdx] * + kPopulationBetweenAge18And65 + unemployed[kArrayIdxCurrentTime]; + int num_workers = static_cast(kStatePopulation) * kPopulationBetweenAge18And65; + num_people_that_can_work[kTimeIndependentArrayIdx] = max( + 0.0, + num_workers - cant_work[kTimeIndependentArrayIdx]); + productivity[kArrayIdxCurrentTime] = + num_people_that_can_work[kTimeIndependentArrayIdx] * + kDailyProductionPerWorker; + + postsubsidy_productivity[kArrayIdxCurrentTime] = + productivity[kArrayIdxCurrentTime] + + subsidy[kArrayIdxCurrentTime]; + } + + // CUDA version of crra_nonlinearity() in + // "ai_economist.foundation.scenarios.covid19_env.py" + __device__ float crra_nonlinearity( + float x, + const float kEta, + const int kNumDaysInAnYear + ) { + float annual_x = kNumDaysInAnYear * x; + float annual_x_clipped = annual_x; + if (annual_x < 0.1) { + annual_x_clipped = 0.1; + } else if (annual_x > 3.0) { + annual_x_clipped = 3.0; + } + float annual_crra = 1 + (pow(annual_x_clipped, (1 - kEta)) - 1) / + (1 - kEta); + float daily_crra = annual_crra / kNumDaysInAnYear; + return daily_crra; + } + + // CUDA version of min_max_normalization() in + // "ai_economist.foundation.scenarios.covid19_env.py" + __device__ float min_max_normalization( + float x, + const float kMinX, + const float kMaxX + ) { + return (x - kMinX) / (kMaxX - kMinX + kEpsilon); + } + + // CUDA version of get_rew() in + // "ai_economist.foundation.scenarios.covid19_env.py" + __device__ float get_rew( + const float kHealthIndexWeightage, + float health_index, + const float kEconomicIndexWeightage, + float economic_index + ) { + return ( + kHealthIndexWeightage * health_index + + kEconomicIndexWeightage * economic_index) / + (kHealthIndexWeightage + kEconomicIndexWeightage); + } + + // CUDA version of scenario_step() in + // "ai_economist.foundation.scenarios.covid19_env.py" + __global__ void CudaCovidAndEconomySimulationStep( + float* susceptible, + float* infected, + float* recovered, + float* deaths, + float* vaccinated, + float* unemployed, + float* subsidy, + float* productivity, + int* stringency_level, + const int kNumStringencyLevels, + float* postsubsidy_productivity, + int* num_vaccines_available_t, + const int* kRealWorldStringencyPolicyHistory, + const int kBetaDelay, + const float* kBetaSlopes, + const float* kbetaIntercepts, + float* beta, + const float kGamma, + const float kDeathRate, + float* incapacitated, + float* cant_work, + float* num_people_that_can_work, + const int* us_kStatePopulation, + const float kInfectionTooSickToWorkRate, + const float kPopulationBetweenAge18And65, + const int kFilterLen, + const int kNumFilters, + int* delta_stringency_level, + const float* kGroupedConvolutionalFilterWeights, + const float* kUnemploymentConvolutionalFilters, + const float* kUnemploymentBias, + float* signal, + const float kDailyProductionPerWorker, + const float* maximum_productivity, + float* obs_a_world_agent_state, + float* obs_a_world_agent_postsubsidy_productivity, + float* obs_a_world_lagged_stringency_level, + float* obs_a_time, + float* obs_p_world_agent_state, + float* obs_p_world_agent_postsubsidy_productivity, + float* obs_p_world_lagged_stringency_level, + float* obs_p_time, + int * env_timestep_arr, + const int kNumAgents, + const int kEpisodeLength + ) { + const int kEnvId = blockIdx.x; + const int kAgentId = threadIdx.x; + + assert(env_timestep_arr[kEnvId] > 0 && + env_timestep_arr[kEnvId] <= kEpisodeLength); + assert (kAgentId <= kNumAgents - 1); + const int kNumFeatures = 6; + + if (kAgentId < (kNumAgents - 1)) { + // Indices for time-dependent and time-independent arrays + // Time dependent arrays have shapes (num_envs, + // kEpisodeLength + 1, kNumAgents - 1) + // Time independent arrays have shapes (num_envs, kNumAgents - 1) + const int kArrayIndexOffset = kEnvId * (kEpisodeLength + 1) * + (kNumAgents - 1); + int kArrayIdxCurrentTime = kArrayIndexOffset + + env_timestep_arr[kEnvId] * (kNumAgents - 1) + kAgentId; + int kArrayIdxPrevTime = kArrayIndexOffset + + (env_timestep_arr[kEnvId] - 1) * (kNumAgents - 1) + kAgentId; + const int kTimeIndependentArrayIdx = kEnvId * + (kNumAgents - 1) + kAgentId; + + const float kStatePopulation = static_cast(us_kStatePopulation[kAgentId]); + + cuda_sir_step( + susceptible, + infected, + recovered, + vaccinated, + deaths, + num_vaccines_available_t, + kRealWorldStringencyPolicyHistory, + kStatePopulation, + kNumAgents, + kBetaDelay, + kBetaSlopes[kAgentId], + kbetaIntercepts[kAgentId], + stringency_level, + beta, + kGamma, + kDeathRate, + kEnvId, + kAgentId, + env_timestep_arr[kEnvId], + kEpisodeLength, + kArrayIdxCurrentTime, + kArrayIdxPrevTime, + kTimeIndependentArrayIdx); + + cuda_unemployment_step( + unemployed, + stringency_level, + delta_stringency_level, + kGroupedConvolutionalFilterWeights, + kUnemploymentConvolutionalFilters, + kUnemploymentBias, + signal, + kFilterLen, + kNumFilters, + kStatePopulation, + kNumAgents, + kEnvId, + kAgentId, + env_timestep_arr[kEnvId], + kArrayIdxCurrentTime, + kArrayIdxPrevTime); + + cuda_economy_step( + infected, + deaths, + unemployed, + incapacitated, + cant_work, + num_people_that_can_work, + kStatePopulation, + kInfectionTooSickToWorkRate, + kPopulationBetweenAge18And65, + kDailyProductionPerWorker, + productivity, + subsidy, + postsubsidy_productivity, + env_timestep_arr[kEnvId], + kArrayIdxCurrentTime, + kTimeIndependentArrayIdx); + + // CUDA version of generate observations + // Agents' observations + int kFeatureArrayIndexOffset = kEnvId * kNumFeatures * + (kNumAgents - 1) + kAgentId; + obs_a_world_agent_state[ + kFeatureArrayIndexOffset + 0 * (kNumAgents - 1) + ] = susceptible[kArrayIdxCurrentTime] / kStatePopulation; + obs_a_world_agent_state[ + kFeatureArrayIndexOffset + 1 * (kNumAgents - 1) + ] = infected[kArrayIdxCurrentTime] / kStatePopulation; + obs_a_world_agent_state[ + kFeatureArrayIndexOffset + 2 * (kNumAgents - 1) + ] = recovered[kArrayIdxCurrentTime] / kStatePopulation; + obs_a_world_agent_state[ + kFeatureArrayIndexOffset + 3 * (kNumAgents - 1) + ] = deaths[kArrayIdxCurrentTime] / kStatePopulation; + obs_a_world_agent_state[ + kFeatureArrayIndexOffset + 4 * (kNumAgents - 1) + ] = vaccinated[kArrayIdxCurrentTime] / kStatePopulation; + obs_a_world_agent_state[ + kFeatureArrayIndexOffset + 5 * (kNumAgents - 1) + ] = unemployed[kArrayIdxCurrentTime] / kStatePopulation; + + for (int feature_id = 0; feature_id < kNumFeatures; feature_id ++) { + const int kIndex = feature_id * (kNumAgents - 1); + obs_p_world_agent_state[kFeatureArrayIndexOffset + + kIndex + ] = obs_a_world_agent_state[kFeatureArrayIndexOffset + + kIndex]; + } + + obs_a_world_agent_postsubsidy_productivity[ + kTimeIndependentArrayIdx + ] = postsubsidy_productivity[kArrayIdxCurrentTime] / + maximum_productivity[kAgentId]; + obs_p_world_agent_postsubsidy_productivity[ + kTimeIndependentArrayIdx + ] = obs_a_world_agent_postsubsidy_productivity[ + kTimeIndependentArrayIdx + ]; + + int t_beta = env_timestep_arr[kEnvId] - kBetaDelay + 1; + if (t_beta < 0) { + obs_a_world_lagged_stringency_level[ + kTimeIndependentArrayIdx + ] = kRealWorldStringencyPolicyHistory[ + env_timestep_arr[kEnvId] * (kNumAgents - 1) + kAgentId + ] / static_cast(kNumStringencyLevels); + } else { + obs_a_world_lagged_stringency_level[ + kTimeIndependentArrayIdx + ] = stringency_level[ + kArrayIndexOffset + + t_beta * (kNumAgents - 1) + + kAgentId + ] / static_cast(kNumStringencyLevels); + } + obs_p_world_lagged_stringency_level[ + kTimeIndependentArrayIdx + ] = obs_a_world_lagged_stringency_level[ + kTimeIndependentArrayIdx]; + // Below, we assume observation scaling = True + // (otherwise, 'obs_a_time[kTimeIndependentArrayIdx] = + // static_cast(env_timestep_arr[kEnvId]) + obs_a_time[kTimeIndependentArrayIdx] = + env_timestep_arr[kEnvId] / static_cast(kEpisodeLength); + } else if (kAgentId == kNumAgents - 1) { + obs_p_time[kEnvId] = env_timestep_arr[kEnvId] / + static_cast(kEpisodeLength); + } + } + + // CUDA version of the compute_reward() in + // "ai_economist.foundation.scenarios.covid19_env.py" + __global__ void CudaComputeReward( + float* rewards_a, + float* rewards_p, + const int kNumDaysInAnYear, + const int kValueOfLife, + const float kRiskFreeInterestRate, + const float kEconomicRewardCrraEta, + const float* kMinMarginalAgentHealthIndex, + const float* kMaxMarginalAgentHealthIndex, + const float* kMinMarginalAgentEconomicIndex, + const float* kMaxMarginalAgentEconomicIndex, + const float kMinMarginalPlannerHealthIndex, + const float kMaxMarginalPlannerHealthIndex, + const float kMinMarginalPlannerEconomicIndex, + const float kMaxMarginalPlannerEconomicIndex, + const float* kWeightageOnMarginalAgentHealthIndex, + const float* kWeightageOnMarginalPlannerHealthIndex, + const float kWeightageOnMarginalAgentEconomicIndex, + const float kWeightageOnMarginalPlannerEconomicIndex, + const float* kAgentsHealthNorm, + const float* kAgentsEconomicNorm, + const float kPlannerHealthNorm, + const float kPlannerEconomicNorm, + float* deaths, + float* subsidy, + float* postsubsidy_productivity, + int* env_done_arr, + int* env_timestep_arr, + const int kNumAgents, + const int kEpisodeLength + ) { + const int kEnvId = blockIdx.x; + const int kAgentId = threadIdx.x; + + assert(env_timestep_arr[kEnvId] > 0 && + env_timestep_arr[kEnvId] <= kEpisodeLength); + assert (kAgentId <= kNumAgents - 1); + + const int kArrayIndexOffset = kEnvId * (kEpisodeLength + 1) * + (kNumAgents - 1); + if (kAgentId < (kNumAgents - 1)) { + // Agents' rewards + // Indices for time-dependent and time-independent arrays + // Time dependent arrays have shapes (num_envs, + // kEpisodeLength + 1, kNumAgents - 1) + // Time independent arrays have shapes (num_envs, kNumAgents - 1) + int kArrayIdxCurrentTime = kArrayIndexOffset + + env_timestep_arr[kEnvId] * (kNumAgents - 1) + kAgentId; + int kArrayIdxPrevTime = kArrayIndexOffset + + (env_timestep_arr[kEnvId] - 1) * (kNumAgents - 1) + kAgentId; + const int kTimeIndependentArrayIdx = kEnvId * + (kNumAgents - 1) + kAgentId; + + float marginal_deaths = deaths[kArrayIdxCurrentTime] - + deaths[kArrayIdxPrevTime]; + + // Note: changing the order of operations to prevent overflow + float marginal_agent_health_index = - marginal_deaths / + (kAgentsHealthNorm[kAgentId] / + static_cast(kValueOfLife)); + + float marginal_agent_economic_index = crra_nonlinearity( + postsubsidy_productivity[kArrayIdxCurrentTime] / + kAgentsEconomicNorm[kAgentId], + kEconomicRewardCrraEta, + kNumDaysInAnYear); + + marginal_agent_health_index = min_max_normalization( + marginal_agent_health_index, + kMinMarginalAgentHealthIndex[kAgentId], + kMaxMarginalAgentHealthIndex[kAgentId]); + marginal_agent_economic_index = min_max_normalization( + marginal_agent_economic_index, + kMinMarginalAgentEconomicIndex[kAgentId], + kMaxMarginalAgentEconomicIndex[kAgentId]); + + rewards_a[kTimeIndependentArrayIdx] = get_rew( + kWeightageOnMarginalAgentHealthIndex[kAgentId], + marginal_agent_health_index, + kWeightageOnMarginalPlannerHealthIndex[kAgentId], + marginal_agent_economic_index); + } else if (kAgentId == kNumAgents - 1) { + // Planner's rewards + float total_marginal_deaths = 0; + for (int ag_id = 0; ag_id < (kNumAgents - 1); ag_id ++) { + total_marginal_deaths += ( + deaths[kArrayIndexOffset + env_timestep_arr[kEnvId] * + (kNumAgents - 1) + ag_id] - + deaths[kArrayIndexOffset + (env_timestep_arr[kEnvId] - 1) * + (kNumAgents - 1) + ag_id]); + } + // Note: changing the order of operations to prevent overflow + float marginal_planner_health_index = -total_marginal_deaths / + (kPlannerHealthNorm / static_cast(kValueOfLife)); + + float total_subsidy = 0.0; + float total_postsubsidy_productivity = 0.0; + for (int ag_id = 0; ag_id < (kNumAgents - 1); ag_id ++) { + total_subsidy += subsidy[kArrayIndexOffset + + env_timestep_arr[kEnvId] * (kNumAgents - 1) + ag_id]; + total_postsubsidy_productivity += + postsubsidy_productivity[kArrayIndexOffset + + env_timestep_arr[kEnvId] * (kNumAgents - 1) + ag_id]; + } + + float cost_of_subsidy = (1 + kRiskFreeInterestRate) * + total_subsidy; + float marginal_planner_economic_index = crra_nonlinearity( + (total_postsubsidy_productivity - cost_of_subsidy) / + kPlannerEconomicNorm, + kEconomicRewardCrraEta, + kNumDaysInAnYear); + + marginal_planner_health_index = min_max_normalization( + marginal_planner_health_index, + kMinMarginalPlannerHealthIndex, + kMaxMarginalPlannerHealthIndex); + marginal_planner_economic_index = min_max_normalization( + marginal_planner_economic_index, + kMinMarginalPlannerEconomicIndex, + kMaxMarginalPlannerEconomicIndex); + + rewards_p[kEnvId] = get_rew( + kWeightageOnMarginalAgentEconomicIndex, + marginal_planner_health_index, + kWeightageOnMarginalPlannerEconomicIndex, + marginal_planner_economic_index); + } + + // Wait here for all agents to finish computing rewards + __syncthreads(); + + // Use only agent 0's thread to set done_arr + if (kAgentId == 0) { + if (env_timestep_arr[kEnvId] == kEpisodeLength) { + env_timestep_arr[kEnvId] = 0; + env_done_arr[kEnvId] = 1; + } + } + } +} diff --git a/ai_economist/foundation/scenarios/covid19/key_to_check_activation_code_against b/ai_economist/foundation/scenarios/covid19/key_to_check_activation_code_against new file mode 100644 index 0000000..2bb74b3 --- /dev/null +++ b/ai_economist/foundation/scenarios/covid19/key_to_check_activation_code_against @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpgIBAAKCAQEAk1+Qz0/Qg4OOGrskBJnVI9KVGTEUvsldHUV4AzLeecYZSV5+ +FZUQpl8lq1mstUZZ0xMlGSHz2t+AAJxyEro8mAj9gAp1qeN58pAX2k29DOt4YRnp +sTF1UG+nrV2aW+jfH16aeVsjWY+Nq+GxGyE3Q5bsxOhnOg0TUaB6RY8SBE/scTHn +bfNsgTc5EuiAAGqYYYdu12n5zeyvfjGW7bBf4Q9t0F0bI+YdZQY9HD35KAoNcqFQ +dvd2vKbojejkn+WyO1amnZxgAhVjpT61FV4u18jPN0Qrt0LHuF5kUVzYal+73ySY +BbwEo4onEn9xvUlQGFJWmv4OPwbI3d4nLqP+mQIHK9xUXfK97QKCAQABeR2EO0uu +ERyRXa5Mh7xsOEq/OJ9sQq+si8B5gDyyM1SW61wQMKF4Wiqw68bMCVvGRwScZD+T +XwBEBJMm9lCVx/UfOWqYSNFCk/YBefv9AI0Kg5lfCMZQuTdjMcbJdjoR5xoiCbO1 +ya7oOU8mfWx/SV0o/698b/zMVBKBBQDNZaN9pmtTOgm3G1QnM9ZlmrdlKYpe9Ihs +3sG4437QaPhumdZi8IoLBGMyYL2O38pG34LJjIkP8Efj1QVTndIIZX8CKghir++j +nUAyofFt7/PBS2k7gQ/1gFISwHxKjmzl/Fc25o7ahlLbO+i2UnRiB9IXcmiGDXMv +tY09oXhxCtTZAoGBAMEkMTzoiqKjXLwKLyFIF5QzXqQKcGqfC8NhQMsm43K0TgHg +Sv1fLdnKw0FWSG30gppBorAY9p5FoI+AWwTSd+AJhz7T1y/shpJx1oBR8qKWO5kO +gMru9kRRb0zb5hydakie3mujz7GUPiXrntKZjC4QYLar0USPulJnU+UTF6QjAoGB +AMNWJqG1ybrk0sNkWJJDW+MnMT0T9o0E+CtbRHqMHh7K1LF9Sc/qh0gLfDo51+kr +pscLaaJiF1Q8phzDhW9QDeNv+4lknNqMFBCFtzns1wVDlXL4U87oqhuBSs6IZAuO +CGVefYKgefdwn64rcyRNala44BbiMJKwRoDvvgH1FvATAoGAV1YK9ZHB1RkXkZ5a +uBePXvkScaujH4DxadMGf2tBuI1wIpVwhxOQ56yDwYoAuexXPUa8BAx2V69/LFo7 +H/yDYqzndA8WwZLy8oy7Ug+fFLtCp7VhkEwMPciBq6KjzUyShIBlgZOx5m5kTbfu +Cs2JQU35YHeompcpLooRG1/cFZkCgYAyVlWABzmgSKJL9ohwlSBBZFCjQ1mjN6uc +uRJxncqfCe3XQ5erFjuWMfPayWONBsWexNucJFc7Iz2LzCOXkUsftldEEET9f/2w +PrbsEu8khNTLqUcow2Whz+A8C0dV6p2cqtTKR1XlSmNVqP30lmpHcmF+R3M/J1ON +K7S9zJJ+zwKBgHIuCATGCGCOzAsUo80OQL46j74SxRV3H1CJASLKzatiTo54dbO6 +86w+N6BfYtYeRlnX1CTGl6bHqVUMBBlKws8Ig3gV3xFS8BiSav8zQ2m99JuhlVHF +Ocfowmuad3WXYvYXQ5IeP2JM/3q7BoPLg1DKP4GGZlNbatMRI+H0HimV +-----END RSA PRIVATE KEY----- \ No newline at end of file diff --git a/ai_economist/foundation/scenarios/one_step_economy/__init__.py b/ai_economist/foundation/scenarios/one_step_economy/__init__.py new file mode 100644 index 0000000..fc6cee4 --- /dev/null +++ b/ai_economist/foundation/scenarios/one_step_economy/__init__.py @@ -0,0 +1,5 @@ +# Copyright (c) 2020, salesforce.com, inc. +# All rights reserved. +# SPDX-License-Identifier: BSD-3-Clause +# For full license text, see the LICENSE file in the repo root +# or https://opensource.org/licenses/BSD-3-Clause diff --git a/ai_economist/foundation/scenarios/one_step_economy/one_step_economy.py b/ai_economist/foundation/scenarios/one_step_economy/one_step_economy.py new file mode 100644 index 0000000..78f1950 --- /dev/null +++ b/ai_economist/foundation/scenarios/one_step_economy/one_step_economy.py @@ -0,0 +1,336 @@ +# Copyright (c) 2021 salesforce.com, inc. +# All rights reserved. +# SPDX-License-Identifier: BSD-3-Clause +# For full license text, see the LICENSE file in the repo root +# or https://opensource.org/licenses/BSD-3-Clause + +import numpy as np + +from ai_economist.foundation.base.base_env import BaseEnvironment, scenario_registry +from ai_economist.foundation.scenarios.utils import rewards, social_metrics + + +@scenario_registry.add +class OneStepEconomy(BaseEnvironment): + """ + A simple model featuring one "step" of setting taxes and earning income. + + As described in https://arxiv.org/abs/2108.02755: + A simplified version of simple_wood_and_stone scenario where both the planner + and the agents each make a single decision: the planner setting taxes and the + agents choosing labor. Each agent chooses an amount of labor that optimizes + its post-tax utility, and this optimal labor depends on its skill and the tax + rates, and it does not depend on the labor choices of other agents. Before + the agents act, the planner sets the marginal tax rates in order to optimize + social welfare. + + Note: + This scenario is intended to be used with the 'PeriodicBracketTax' and + 'SimpleLabor' components. + It should use an episode length of 2. In the first step, taxes are set by + the planner via 'PeriodicBracketTax'. In the second, agents select how much + to work/earn via 'SimpleLabor'. + + Args: + agent_reward_type (str): The type of utility function used to compute each + agent's reward. Defaults to "coin_minus_labor_cost". + isoelastic_eta (float): The shape parameter of the isoelastic function used + in the "isoelastic_coin_minus_labor" utility function. + labor_exponent (float): The labor exponent parameter used in the + "coin_minus_labor_cost" utility function. + labor_cost (float): The coefficient used to weight the cost of labor. + planner_reward_type (str): The type of social welfare function (SWF) used to + compute the planner's reward. Defaults to "inv_income_weighted_utility". + mixing_weight_gini_vs_coin (float): Must be between 0 and 1 (inclusive). + Controls the weighting of equality and productivity when using SWF + "coin_eq_times_productivity", where a value of 0 (default) yields equal + weighting, and 1 only considers productivity. + """ + + name = "one-step-economy" + agent_subclasses = ["BasicMobileAgent", "BasicPlanner"] + required_entities = ["Coin"] + + def __init__( + self, + *base_env_args, + agent_reward_type="coin_minus_labor_cost", + isoelastic_eta=0.23, + labor_exponent=2.0, + labor_cost=1.0, + planner_reward_type="inv_income_weighted_utility", + mixing_weight_gini_vs_coin=0, + **base_env_kwargs + ): + super().__init__(*base_env_args, **base_env_kwargs) + + self.num_agents = len(self.world.agents) + + self.labor_cost = labor_cost + self.agent_reward_type = agent_reward_type + self.isoelastic_eta = isoelastic_eta + self.labor_exponent = labor_exponent + self.planner_reward_type = planner_reward_type + self.mixing_weight_gini_vs_coin = mixing_weight_gini_vs_coin + self.planner_starting_coin = 0 + + self.curr_optimization_metrics = {str(a.idx): 0 for a in self.all_agents} + + # The following methods must be implemented for each scenario + # ----------------------------------------------------------- + def reset_starting_layout(self): + """ + Part 1/2 of scenario reset. This method handles resetting the state of the + environment managed by the scenario (i.e. resource & landmark layout). + + Here, generate a resource source layout consistent with target parameters. + """ + + def reset_agent_states(self): + """ + Part 2/2 of scenario reset. This method handles resetting the state of the + agents themselves (i.e. inventory, locations, etc.). + + Here, empty inventories, give mobile agents any starting coin, and place them + in random accesible locations to start. + """ + self.world.clear_agent_locs() + + for agent in self.world.agents: + # Clear everything to start with + agent.state["inventory"] = {k: 0 for k in agent.state["inventory"].keys()} + agent.state["escrow"] = {k: 0 for k in agent.state["escrow"].keys()} + agent.state["endogenous"] = {k: 0 for k in agent.state["endogenous"].keys()} + + self.world.planner.inventory["Coin"] = self.planner_starting_coin + + def scenario_step(self): + """ + Update the state of the world according to whatever rules this scenario + implements. + + This gets called in the 'step' method (of base_env) after going through each + component step and before generating observations, rewards, etc. + + NOTE: does not take agent actions into account. + """ + + def generate_observations(self): + """ + Generate observations associated with this scenario. + + A scenario does not need to produce observations and can provide observations + for only some agent types; however, for a given agent type, it should either + always or never yield an observation. If it does yield an observation, + that observation should always have the same structure/sizes! + + Returns: + obs (dict): A dictionary of {agent.idx: agent_obs_dict}. In words, + return a dictionary with an entry for each agent (which can including + the planner) for which this scenario provides an observation. For each + entry, the key specifies the index of the agent and the value contains + its associated observation dictionary. + + Here, non-planner agents receive spatial observations (depending on the env + config) as well as the contents of their inventory and endogenous quantities. + The planner also receives spatial observations (again, depending on the env + config) as well as the inventory of each of the mobile agents. + """ + obs_dict = dict() + for agent in self.world.agents: + obs_dict[str(agent.idx)] = {} + + coin_endowments = np.array( + [agent.total_endowment("Coin") for agent in self.world.agents] + ) + equality = social_metrics.get_equality(coin_endowments) + productivity = social_metrics.get_productivity(coin_endowments) + normalized_per_capita_productivity = productivity / self.num_agents / 1000 + obs_dict[self.world.planner.idx] = { + "normalized_per_capita_productivity": normalized_per_capita_productivity, + "equality": equality, + } + + return obs_dict + + def compute_reward(self): + """ + Apply the reward function(s) associated with this scenario to get the rewards + from this step. + + Returns: + rew (dict): A dictionary of {agent.idx: agent_obs_dict}. In words, + return a dictionary with an entry for each agent in the environment + (including the planner). For each entry, the key specifies the index of + the agent and the value contains the scalar reward earned this timestep. + + Rewards are computed as the marginal utility (agents) or marginal social + welfare (planner) experienced on this timestep. Ignoring discounting, + this means that agents' (planner's) objective is to maximize the utility + (social welfare) associated with the terminal state of the episode. + """ + curr_optimization_metrics = self.get_current_optimization_metrics( + self.world.agents, + isoelastic_eta=float(self.isoelastic_eta), + labor_exponent=float(self.labor_exponent), + labor_coefficient=float(self.labor_cost), + ) + planner_agents_rew = { + k: v - self.curr_optimization_metrics[k] + for k, v in curr_optimization_metrics.items() + } + self.curr_optimization_metrics = curr_optimization_metrics + return planner_agents_rew + + # Optional methods for customization + # ---------------------------------- + def additional_reset_steps(self): + """ + Extra scenario-specific steps that should be performed at the end of the reset + cycle. + + For each reset cycle... + First, reset_starting_layout() and reset_agent_states() will be called. + + Second, .reset() will be called for each registered component. + + Lastly, this method will be called to allow for any final customization of + the reset cycle. + """ + self.curr_optimization_metrics = self.get_current_optimization_metrics( + self.world.agents, + isoelastic_eta=float(self.isoelastic_eta), + labor_exponent=float(self.labor_exponent), + labor_coefficient=float(self.labor_cost), + ) + + def scenario_metrics(self): + """ + Allows the scenario to generate metrics (collected along with component metrics + in the 'metrics' property). + + To have the scenario add metrics, this function needs to return a dictionary of + {metric_key: value} where 'value' is a scalar (no nesting or lists!) + + Here, summarize social metrics, endowments, utilities, and labor cost annealing. + """ + metrics = dict() + + # Log social/economic indicators + coin_endowments = np.array( + [agent.total_endowment("Coin") for agent in self.world.agents] + ) + pretax_incomes = np.array( + [agent.state["production"] for agent in self.world.agents] + ) + metrics["social/productivity"] = social_metrics.get_productivity( + coin_endowments + ) + metrics["social/equality"] = social_metrics.get_equality(coin_endowments) + + utilities = np.array( + [self.curr_optimization_metrics[agent.idx] for agent in self.world.agents] + ) + metrics[ + "social_welfare/coin_eq_times_productivity" + ] = rewards.coin_eq_times_productivity( + coin_endowments=coin_endowments, equality_weight=1.0 + ) + metrics[ + "social_welfare/inv_income_weighted_utility" + ] = rewards.inv_income_weighted_utility( + coin_endowments=pretax_incomes, utilities=utilities # coin_endowments, + ) + + # Log average endowments, endogenous, and utility for agents + agent_endows = {} + agent_endogenous = {} + agent_utilities = [] + for agent in self.world.agents: + for resource in agent.inventory.keys(): + if resource not in agent_endows: + agent_endows[resource] = [] + agent_endows[resource].append( + agent.inventory[resource] + agent.escrow[resource] + ) + + for endogenous, quantity in agent.endogenous.items(): + if endogenous not in agent_endogenous: + agent_endogenous[endogenous] = [] + agent_endogenous[endogenous].append(quantity) + + agent_utilities.append(self.curr_optimization_metrics[agent.idx]) + + for resource, quantities in agent_endows.items(): + metrics["endow/avg_agent/{}".format(resource)] = np.mean(quantities) + + for endogenous, quantities in agent_endogenous.items(): + metrics["endogenous/avg_agent/{}".format(endogenous)] = np.mean(quantities) + + metrics["util/avg_agent"] = np.mean(agent_utilities) + + # Log endowments and utility for the planner + for resource, quantity in self.world.planner.inventory.items(): + metrics["endow/p/{}".format(resource)] = quantity + + metrics["util/p"] = self.curr_optimization_metrics[self.world.planner.idx] + + return metrics + + def get_current_optimization_metrics( + self, agents, isoelastic_eta=0.23, labor_exponent=2.0, labor_coefficient=0.1 + ): + """ + Compute optimization metrics based on the current state. Used to compute reward. + + Returns: + curr_optimization_metric (dict): A dictionary of {agent.idx: metric} + with an entry for each agent (including the planner) in the env. + """ + curr_optimization_metric = {} + + coin_endowments = np.array([agent.total_endowment("Coin") for agent in agents]) + + pretax_incomes = np.array([agent.state["production"] for agent in agents]) + + # Optimization metric for agents: + for agent in agents: + if self.agent_reward_type == "isoelastic_coin_minus_labor": + assert 0.0 <= isoelastic_eta <= 1.0 + curr_optimization_metric[ + agent.idx + ] = rewards.isoelastic_coin_minus_labor( + coin_endowment=agent.total_endowment("Coin"), + total_labor=agent.state["endogenous"]["Labor"], + isoelastic_eta=isoelastic_eta, + labor_coefficient=labor_coefficient, + ) + elif self.agent_reward_type == "coin_minus_labor_cost": + assert labor_exponent > 1.0 + curr_optimization_metric[agent.idx] = rewards.coin_minus_labor_cost( + coin_endowment=agent.total_endowment("Coin"), + total_labor=agent.state["endogenous"]["Labor"], + labor_exponent=labor_exponent, + labor_coefficient=labor_coefficient, + ) + # Optimization metric for the planner: + if self.planner_reward_type == "coin_eq_times_productivity": + curr_optimization_metric[ + self.world.planner.idx + ] = rewards.coin_eq_times_productivity( + coin_endowments=coin_endowments, + equality_weight=1 - self.mixing_weight_gini_vs_coin, + ) + elif self.planner_reward_type == "inv_income_weighted_utility": + curr_optimization_metric[ + self.world.planner.idx + ] = rewards.inv_income_weighted_utility( + coin_endowments=pretax_incomes, # coin_endowments, + utilities=np.array( + [curr_optimization_metric[agent.idx] for agent in agents] + ), + ) + else: + print("No valid planner reward selected!") + raise NotImplementedError + return curr_optimization_metric diff --git a/ai_economist/foundation/scenarios/simple_wood_and_stone/__init__.py b/ai_economist/foundation/scenarios/simple_wood_and_stone/__init__.py new file mode 100644 index 0000000..fc6cee4 --- /dev/null +++ b/ai_economist/foundation/scenarios/simple_wood_and_stone/__init__.py @@ -0,0 +1,5 @@ +# Copyright (c) 2020, salesforce.com, inc. +# All rights reserved. +# SPDX-License-Identifier: BSD-3-Clause +# For full license text, see the LICENSE file in the repo root +# or https://opensource.org/licenses/BSD-3-Clause diff --git a/ai_economist/foundation/scenarios/simple_wood_and_stone/dynamic_layout.py b/ai_economist/foundation/scenarios/simple_wood_and_stone/dynamic_layout.py new file mode 100644 index 0000000..e6b8edc --- /dev/null +++ b/ai_economist/foundation/scenarios/simple_wood_and_stone/dynamic_layout.py @@ -0,0 +1,1021 @@ +# Copyright (c) 2020, salesforce.com, inc. +# All rights reserved. +# SPDX-License-Identifier: BSD-3-Clause +# For full license text, see the LICENSE file in the repo root +# or https://opensource.org/licenses/BSD-3-Clause + +from copy import deepcopy + +import numpy as np +from scipy import signal + +from ai_economist.foundation.base.base_env import BaseEnvironment, scenario_registry +from ai_economist.foundation.scenarios.utils import rewards, social_metrics + + +@scenario_registry.add +class Uniform(BaseEnvironment): + """ + World containing spatially-segregated stone and wood with stochastic regeneration. + + For controlling how resource regeneration behavior... + Coverage: if fraction, target fraction of total tiles; if integer, target number + of tiles + Regen Halfwidth: width of regen kernel = 1 + (2 * halfwidth); set >0 to create + a spatial social dilemma + Regen Weight: regen probability per tile counted by the regen kernel + Max Health: how many resource units can populate a source block + Clumpiness: degree to which resources are spatially clustered + Gradient Steepness: degree to which stone/wood are restricted to the top/bottom + of the map + + Args: + planner_gets_spatial_obs (bool): Whether the planner agent receives spatial + observations from the world. + full_observability (bool): Whether the mobile agents' spatial observation + includes the full world view or is instead an egocentric view. + mobile_agent_observation_range (int): If not using full_observability, + the spatial range (on each side of the agent) that is visible in the + spatial observations. + starting_wood_coverage (int, float): Target coverage of wood at t=0. + wood_regen_halfwidth (int): Regen halfwidth for wood. + wood_regen_weight (float): Regen weight for wood. + wood_max_health (int): Max wood units per wood source tile. + wood_clumpiness (float): Degree of wood clumping. + starting_stone_coverage (int, float): Target coverage of stone at t=0. + stone_regen_halfwidth (int): Regen halfwidth for stone. + stone_regen_weight (float): Regen weight for stone. + stone_max_health (int): Max stone units per stone source tile. + stone_clumpiness (float): Degree of stone clumping. + gradient_steepness (int, float): How steeply source tile probability falls + off from the top/bottom of the map. + checker_source_blocks (bool): Whether to space source tiles in a "checker" + formation. + starting_agent_coin (int, float): Amount of coin agents have at t=0. Defaults + to zero coin. + isoelastic_eta (float): Parameter controlling the shape of agent utility + wrt coin endowment. + energy_cost (float): Coefficient for converting labor to negative utility. + energy_warmup_constant (float): Decay constant that controls the rate at which + the effective energy cost is annealed from 0 to energy_cost. Set to 0 + (default) to disable annealing, meaning that the effective energy cost is + always energy_cost. The units of the decay constant depend on the choice of + energy_warmup_method. + energy_warmup_method (str): How to schedule energy annealing (warmup). If + "decay" (default), use the number of completed episodes. If "auto", + use the number of timesteps where the average agent reward was positive. + planner_reward_type (str): The type of reward used for the planner. Options + are "coin_eq_times_productivity" (default), + "inv_income_weighted_coin_endowment", and "inv_income_weighted_utility". + mixing_weight_gini_vs_coin (float): Degree to which equality is ignored w/ + "coin_eq_times_productivity". Default is 0, which weights equality and + productivity equally. If set to 1, only productivity is rewarded. + + """ + + name = "uniform/simple_wood_and_stone" + agent_subclasses = ["BasicMobileAgent", "BasicPlanner"] + required_entities = ["Wood", "Stone"] + + def __init__( + self, + *base_env_args, + planner_gets_spatial_info=True, + full_observability=False, + mobile_agent_observation_range=5, + starting_wood_coverage=0.025, + wood_regen_halfwidth=0, + wood_regen_weight=0.01, + wood_max_health=1, + starting_stone_coverage=0.025, + stone_regen_halfwidth=0, + stone_regen_weight=0.01, + stone_max_health=1, + wood_clumpiness=0.35, + stone_clumpiness=0.5, + gradient_steepness=8, + checker_source_blocks=False, + starting_agent_coin=0, + isoelastic_eta=0.23, + energy_cost=0.21, + energy_warmup_constant=0, + energy_warmup_method="decay", + planner_reward_type="coin_eq_times_productivity", + mixing_weight_gini_vs_coin=0.0, + **base_env_kwargs + ): + super().__init__(*base_env_args, **base_env_kwargs) + + # Whether agents receive spatial information in their observation tensor + self._planner_gets_spatial_info = bool(planner_gets_spatial_info) + + # Whether the (non-planner) agents can see the whole world map + self._full_observability = bool(full_observability) + + self._mobile_agent_observation_range = int(mobile_agent_observation_range) + + # For controlling how resource regeneration behavior + # - Coverage: if fraction, target fraction of total tiles; + # if integer, target number of tiles + # - Regen Halfwidth: width of regen kernel = 1 + (2 * halfwidth) + # - Regen Weight: regen probability per tile counted by the regen kernel + # - Max Health: how many resource units can populate a source block + # - Clumpiness: degree to which resources are spatially clustered + # - Gradient Steepness: degree to which stone/wood + # are restricted to top/bottom of map + self.layout_specs = dict(Wood={}, Stone={}) + # + if starting_wood_coverage >= 1: + starting_wood_coverage /= np.prod(self.world_size) + if starting_stone_coverage >= 1: + starting_stone_coverage /= np.prod(self.world_size) + assert (starting_stone_coverage + starting_wood_coverage) < 0.5 + # + self._checker_source_blocks = bool(checker_source_blocks) + c, r = np.meshgrid( + np.arange(self.world_size[1]) % 2, np.arange(self.world_size[0]) % 2 + ) + self._checker_mask = (r + c) == 1 + m = 2 if self._checker_source_blocks else 1 + # + self.layout_specs["Wood"]["starting_coverage"] = ( + float(starting_wood_coverage) * m + ) + self.layout_specs["Stone"]["starting_coverage"] = ( + float(starting_stone_coverage) * m + ) + assert 0 < self.layout_specs["Wood"]["starting_coverage"] < 1 + assert 0 < self.layout_specs["Stone"]["starting_coverage"] < 1 + # + self.layout_specs["Wood"]["regen_halfwidth"] = int(wood_regen_halfwidth) + self.layout_specs["Stone"]["regen_halfwidth"] = int(stone_regen_halfwidth) + assert 0 <= self.layout_specs["Wood"]["regen_halfwidth"] <= 3 + assert 0 <= self.layout_specs["Stone"]["regen_halfwidth"] <= 3 + # + self.layout_specs["Wood"]["regen_weight"] = float(wood_regen_weight) + self.layout_specs["Stone"]["regen_weight"] = float(stone_regen_weight) + assert 0 <= self.layout_specs["Wood"]["regen_weight"] <= 1 + assert 0 <= self.layout_specs["Stone"]["regen_weight"] <= 1 + # + self.layout_specs["Wood"]["max_health"] = int(wood_max_health) + self.layout_specs["Stone"]["max_health"] = int(stone_max_health) + assert self.layout_specs["Wood"]["max_health"] > 0 + assert self.layout_specs["Stone"]["max_health"] > 0 + # + self.clumpiness = { + "Wood": float(wood_clumpiness), + "Stone": float(stone_clumpiness), + } + assert all(0 <= v <= 1 for v in self.clumpiness.values()) + # + self.gradient_steepness = float(gradient_steepness) + assert self.gradient_steepness >= 1.0 + # + self.source_prob_maps = self.make_source_prob_maps() + self.source_maps = { + k: np.zeros_like(v) for k, v in self.source_prob_maps.items() + } + + # How much coin do agents begin with at upon reset + self.starting_agent_coin = float(starting_agent_coin) + assert self.starting_agent_coin >= 0.0 + + # Controls the diminishing marginal utility of coin. + # isoelastic_eta=0 means no diminishing utility. + self.isoelastic_eta = float(isoelastic_eta) + assert 0.0 <= self.isoelastic_eta <= 1.0 + + # The amount that labor is weighted in utility computation + # (once annealing is finished) + self.energy_cost = float(energy_cost) + assert self.energy_cost >= 0 + + # What value to use for calculating the progress of energy annealing + # If method = 'decay': #completed episodes + # If method = 'auto' : #timesteps where avg. agent reward > 0 + self.energy_warmup_method = energy_warmup_method.lower() + assert self.energy_warmup_method in ["decay", "auto"] + # Decay constant for annealing to full energy cost + # (if energy_warmup_constant == 0, there is no annealing) + self.energy_warmup_constant = float(energy_warmup_constant) + assert self.energy_warmup_constant >= 0 + self._auto_warmup_integrator = 0 + + # Which social welfare function to use + self.planner_reward_type = str(planner_reward_type).lower() + + # How much to weight equality if using SWF=eq*prod: + # 0 -> SWF=eq*prod + # 1 -> SWF=prod + self.mixing_weight_gini_vs_coin = float(mixing_weight_gini_vs_coin) + assert 0 <= self.mixing_weight_gini_vs_coin <= 1.0 + + # Use this to calculate marginal changes and deliver that as reward + self.init_optimization_metric = {agent.idx: 0 for agent in self.all_agents} + self.prev_optimization_metric = {agent.idx: 0 for agent in self.all_agents} + self.curr_optimization_metric = {agent.idx: 0 for agent in self.all_agents} + + @property + def energy_weight(self): + """ + Energy annealing progress. Multiply with self.energy_cost to get the + effective energy coefficient. + """ + if self.energy_warmup_constant <= 0.0: + return 1.0 + + if self.energy_warmup_method == "decay": + return float(1.0 - np.exp(-self._completions / self.energy_warmup_constant)) + + if self.energy_warmup_method == "auto": + return float( + 1.0 + - np.exp(-self._auto_warmup_integrator / self.energy_warmup_constant) + ) + + raise NotImplementedError + + def get_current_optimization_metrics(self): + """ + Compute optimization metrics based on the current state. Used to compute reward. + + Returns: + curr_optimization_metric (dict): A dictionary of {agent.idx: metric} + with an entry for each agent (including the planner) in the env. + """ + curr_optimization_metric = {} + # (for agents) + for agent in self.world.agents: + curr_optimization_metric[agent.idx] = rewards.isoelastic_coin_minus_labor( + coin_endowment=agent.total_endowment("Coin"), + total_labor=agent.state["endogenous"]["Labor"], + isoelastic_eta=self.isoelastic_eta, + labor_coefficient=self.energy_weight * self.energy_cost, + ) + # (for the planner) + if self.planner_reward_type == "coin_eq_times_productivity": + curr_optimization_metric[ + self.world.planner.idx + ] = rewards.coin_eq_times_productivity( + coin_endowments=np.array( + [agent.total_endowment("Coin") for agent in self.world.agents] + ), + equality_weight=1 - self.mixing_weight_gini_vs_coin, + ) + elif self.planner_reward_type == "inv_income_weighted_coin_endowments": + curr_optimization_metric[ + self.world.planner.idx + ] = rewards.inv_income_weighted_coin_endowments( + coin_endowments=np.array( + [agent.total_endowment("Coin") for agent in self.world.agents] + ) + ) + elif self.planner_reward_type == "inv_income_weighted_utility": + curr_optimization_metric[ + self.world.planner.idx + ] = rewards.inv_income_weighted_utility( + coin_endowments=np.array( + [agent.total_endowment("Coin") for agent in self.world.agents] + ), + utilities=np.array( + [curr_optimization_metric[agent.idx] for agent in self.world.agents] + ), + ) + else: + print("No valid planner reward selected!") + raise NotImplementedError + return curr_optimization_metric + + def make_source_prob_maps(self): + """ + Make maps specifying how likely each location is to be assigned as a resource + source tile. + + Returns: + source_prob_maps (dict): Contains a source probability map for both + stone and wood + """ + prob_gradient = ( + np.arange(self.world_size[0])[:, None].repeat(self.world_size[1], axis=1) + ** self.gradient_steepness + ) + prob_gradient = prob_gradient / np.mean(prob_gradient) + + return { + "Wood": prob_gradient * self.layout_specs["Wood"]["starting_coverage"], + "Stone": prob_gradient[-1::-1] + * self.layout_specs["Wood"]["starting_coverage"], + } + + # The following methods must be implemented for each scenario + # ----------------------------------------------------------- + + def reset_starting_layout(self): + """ + Part 1/2 of scenario reset. This method handles resetting the state of the + environment managed by the scenario (i.e. resource & landmark layout). + + Here, generate a resource source layout consistent with target parameters. + """ + happy_coverage = False + n_reset_tries = 0 + + # Attempt to do a reset until an attempt limit is reached or coverage is good + while n_reset_tries < 100 and not happy_coverage: + self.world.maps.clear() + + self.source_maps = { + k: np.zeros_like(v) for k, v in self.source_prob_maps.items() + } + + resources = ["Wood", "Stone"] + + for resource in resources: + clump = 1 - np.clip(self.clumpiness[resource], 0.0, 0.99) + + source_prob = self.source_prob_maps[resource] * 0.1 * clump + + empty = self.world.maps.empty + + tmp = np.random.rand(*source_prob.shape) + maybe_source_map = (tmp < source_prob) * empty + + n_tries = 0 + while np.mean(maybe_source_map) < ( + self.layout_specs[resource]["starting_coverage"] * clump + ): + tmp *= 0.9 + maybe_source_map = (tmp < source_prob) * empty + n_tries += 1 + if n_tries > 200: + break + + while ( + np.mean(maybe_source_map) + < self.layout_specs[resource]["starting_coverage"] + ): + kernel = np.random.randn(7, 7) > 0 + tmp = signal.convolve2d( + maybe_source_map + + (0.2 * np.random.randn(*maybe_source_map.shape)) + - 0.25, + kernel.astype(np.float32), + "same", + ) + maybe_source_map = np.maximum(tmp > 0, maybe_source_map) * empty + + self.source_maps[resource] = maybe_source_map + self.world.maps.set( + resource, maybe_source_map + ) # * self.layout_specs[resource]['max_health']) + self.world.maps.set(resource + "SourceBlock", maybe_source_map) + + # Restart if the resource distribution is too far off the target coverage + happy_coverage = True + for resource in resources: + coverage_quotient = ( + np.mean(self.source_maps[resource]) + / self.layout_specs[resource]["starting_coverage"] + ) + bound = 0.4 + if not (1 / (1 + bound)) <= coverage_quotient <= (1 + bound): + happy_coverage = False + + n_reset_tries += 1 + + # Apply checkering, if applicable + if self._checker_source_blocks: + for resource, source_map in self.source_maps.items(): + source_map = source_map * self._checker_mask + self.source_maps[resource] = source_map + self.world.maps.set(resource, source_map) + self.world.maps.set(resource + "SourceBlock", source_map) + + def reset_agent_states(self): + """ + Part 2/2 of scenario reset. This method handles resetting the state of the + agents themselves (i.e. inventory, locations, etc.). + + Here, empty inventories, give mobile agents any starting coin, and place them + in random accessible locations to start. + """ + self.world.clear_agent_locs() + + for agent in self.world.agents: + # Clear everything to start with + agent.state["inventory"] = {k: 0 for k in agent.inventory.keys()} + agent.state["escrow"] = {k: 0 for k in agent.inventory.keys()} + agent.state["endogenous"] = {k: 0 for k in agent.endogenous.keys()} + # Add starting coin + agent.state["inventory"]["Coin"] = float(self.starting_agent_coin) + + # Clear everything for the planner + self.world.planner.state["inventory"] = { + k: 0 for k in self.world.planner.inventory.keys() + } + self.world.planner.state["escrow"] = { + k: 0 for k in self.world.planner.escrow.keys() + } + + # Place the agents randomly in the world + for agent in self.world.get_random_order_agents(): + r = np.random.randint(0, self.world_size[0]) + c = np.random.randint(0, self.world_size[1]) + n_tries = 0 + while not self.world.can_agent_occupy(r, c, agent): + r = np.random.randint(0, self.world_size[0]) + c = np.random.randint(0, self.world_size[1]) + n_tries += 1 + if n_tries > 200: + raise TimeoutError + self.world.set_agent_loc(agent, r, c) + + def scenario_step(self): + """ + Update the state of the world according to whatever rules this scenario + implements. + + This gets called in the 'step' method (of base_env) after going through each + component step and before generating observations, rewards, etc. + + In this class of scenarios, the scenario step handles stochastic resource + regeneration. + """ + + resources = ["Wood", "Stone"] + + for resource in resources: + d = 1 + (2 * self.layout_specs[resource]["regen_halfwidth"]) + kernel = ( + self.layout_specs[resource]["regen_weight"] * np.ones((d, d)) / (d ** 2) + ) + + resource_map = self.world.maps.get(resource) + resource_source_blocks = self.world.maps.get(resource + "SourceBlock") + spawnable = ( + self.world.maps.empty + resource_map + resource_source_blocks + ) > 0 + spawnable *= resource_source_blocks > 0 + + health = np.maximum(resource_map, resource_source_blocks) + respawn = np.random.rand(*health.shape) < signal.convolve2d( + health, kernel, "same" + ) + respawn *= spawnable + + self.world.maps.set( + resource, + np.minimum( + resource_map + respawn, self.layout_specs[resource]["max_health"] + ), + ) + + def generate_observations(self): + """ + Generate observations associated with this scenario. + + A scenario does not need to produce observations and can provide observations + for only some agent types; however, for a given agent type, it should either + always or never yield an observation. If it does yield an observation, + that observation should always have the same structure/sizes! + + Returns: + obs (dict): A dictionary of {agent.idx: agent_obs_dict}. In words, + return a dictionary with an entry for each agent (which can including + the planner) for which this scenario provides an observation. For each + entry, the key specifies the index of the agent and the value contains + its associated observation dictionary. + + Here, non-planner agents receive spatial observations (depending on the env + config) as well as the contents of their inventory and endogenous quantities. + The planner also receives spatial observations (again, depending on the env + config) as well as the inventory of each of the mobile agents. + """ + obs = {} + curr_map = self.world.maps.state + + owner_map = self.world.maps.owner_state + loc_map = self.world.loc_map + agent_idx_maps = np.concatenate([owner_map, loc_map[None, :, :]], axis=0) + agent_idx_maps += 2 + agent_idx_maps[agent_idx_maps == 1] = 0 + + agent_locs = { + str(agent.idx): { + "loc-row": agent.loc[0] / self.world_size[0], + "loc-col": agent.loc[1] / self.world_size[1], + } + for agent in self.world.agents + } + agent_invs = { + str(agent.idx): { + "inventory-" + k: v * self.inv_scale for k, v in agent.inventory.items() + } + for agent in self.world.agents + } + + obs[self.world.planner.idx] = { + "inventory-" + k: v * self.inv_scale + for k, v in self.world.planner.inventory.items() + } + if self._planner_gets_spatial_info: + obs[self.world.planner.idx].update( + dict(map=curr_map, idx_map=agent_idx_maps) + ) + + # Mobile agents see the full map. Convey location info via one-hot map channels. + if self._full_observability: + for agent in self.world.agents: + my_map = np.array(agent_idx_maps) + my_map[my_map == int(agent.idx) + 2] = 1 + sidx = str(agent.idx) + obs[sidx] = {"map": curr_map, "idx_map": my_map} + obs[sidx].update(agent_invs[sidx]) + + # Mobile agents only see within a window around their position + else: + w = ( + self._mobile_agent_observation_range + ) # View halfwidth (only applicable without full observability) + + padded_map = np.pad( + curr_map, + [(0, 1), (w, w), (w, w)], + mode="constant", + constant_values=[(0, 1), (0, 0), (0, 0)], + ) + + padded_idx = np.pad( + agent_idx_maps, + [(0, 0), (w, w), (w, w)], + mode="constant", + constant_values=[(0, 0), (0, 0), (0, 0)], + ) + + for agent in self.world.agents: + r, c = [c + w for c in agent.loc] + visible_map = padded_map[ + :, (r - w) : (r + w + 1), (c - w) : (c + w + 1) + ] + visible_idx = np.array( + padded_idx[:, (r - w) : (r + w + 1), (c - w) : (c + w + 1)] + ) + + visible_idx[visible_idx == int(agent.idx) + 2] = 1 + + sidx = str(agent.idx) + + obs[sidx] = {"map": visible_map, "idx_map": visible_idx} + obs[sidx].update(agent_locs[sidx]) + obs[sidx].update(agent_invs[sidx]) + + # Agent-wise planner info (gets crunched into the planner obs in the + # base scenario code) + obs["p" + sidx] = agent_invs[sidx] + if self._planner_gets_spatial_info: + obs["p" + sidx].update(agent_locs[sidx]) + + return obs + + def compute_reward(self): + """ + Apply the reward function(s) associated with this scenario to get the rewards + from this step. + + Returns: + rew (dict): A dictionary of {agent.idx: agent_obs_dict}. In words, + return a dictionary with an entry for each agent in the environment + (including the planner). For each entry, the key specifies the index of + the agent and the value contains the scalar reward earned this timestep. + + Rewards are computed as the marginal utility (agents) or marginal social + welfare (planner) experienced on this timestep. Ignoring discounting, + this means that agents' (planner's) objective is to maximize the utility + (social welfare) associated with the terminal state of the episode. + """ + + # "curr_optimization_metric" hasn't been updated yet, so it gives us the + # utility from the last step. + utility_at_end_of_last_time_step = deepcopy(self.curr_optimization_metric) + + # compute current objectives and store the values + self.curr_optimization_metric = self.get_current_optimization_metrics() + + # reward = curr - prev objectives + rew = { + k: float(v - utility_at_end_of_last_time_step[k]) + for k, v in self.curr_optimization_metric.items() + } + + # store the previous objective values + self.prev_optimization_metric.update(utility_at_end_of_last_time_step) + + # Automatic Energy Cost Annealing + # ------------------------------- + avg_agent_rew = np.mean([rew[a.idx] for a in self.world.agents]) + # Count the number of timesteps where the avg agent reward was > 0 + if avg_agent_rew > 0: + self._auto_warmup_integrator += 1 + + return rew + + # Optional methods for customization + # ---------------------------------- + + def additional_reset_steps(self): + """ + Extra scenario-specific steps that should be performed at the end of the reset + cycle. + + For each reset cycle... + First, reset_starting_layout() and reset_agent_states() will be called. + + Second, .reset() will be called for each registered component. + + Lastly, this method will be called to allow for any final customization of + the reset cycle. + + For this scenario, this method resets optimization metric trackers. + """ + # compute current objectives + curr_optimization_metric = self.get_current_optimization_metrics() + + self.curr_optimization_metric = deepcopy(curr_optimization_metric) + self.init_optimization_metric = deepcopy(curr_optimization_metric) + self.prev_optimization_metric = deepcopy(curr_optimization_metric) + + def scenario_metrics(self): + """ + Allows the scenario to generate metrics (collected along with component metrics + in the 'metrics' property). + + To have the scenario add metrics, this function needs to return a dictionary of + {metric_key: value} where 'value' is a scalar (no nesting or lists!) + + Here, summarize social metrics, endowments, utilities, and labor cost annealing + """ + metrics = dict() + + coin_endowments = np.array( + [agent.total_endowment("Coin") for agent in self.world.agents] + ) + metrics["social/productivity"] = social_metrics.get_productivity( + coin_endowments + ) + metrics["social/equality"] = social_metrics.get_equality(coin_endowments) + + utilities = np.array( + [self.curr_optimization_metric[agent.idx] for agent in self.world.agents] + ) + metrics[ + "social_welfare/coin_eq_times_productivity" + ] = rewards.coin_eq_times_productivity( + coin_endowments=coin_endowments, equality_weight=1.0 + ) + metrics[ + "social_welfare/inv_income_weighted_coin_endow" + ] = rewards.inv_income_weighted_coin_endowments(coin_endowments=coin_endowments) + metrics[ + "social_welfare/inv_income_weighted_utility" + ] = rewards.inv_income_weighted_utility( + coin_endowments=coin_endowments, utilities=utilities + ) + + for agent in self.all_agents: + for resource, quantity in agent.inventory.items(): + metrics[ + "endow/{}/{}".format(agent.idx, resource) + ] = agent.total_endowment(resource) + + if agent.endogenous is not None: + for resource, quantity in agent.endogenous.items(): + metrics["endogenous/{}/{}".format(agent.idx, resource)] = quantity + + metrics["util/{}".format(agent.idx)] = self.curr_optimization_metric[ + agent.idx + ] + + # Labor weight + metrics["labor/weighted_cost"] = self.energy_cost * self.energy_weight + metrics["labor/warmup_integrator"] = int(self._auto_warmup_integrator) + + return metrics + + +@scenario_registry.add +class MultiZone(Uniform): + """ + World containing stone and wood clustered in "zones" with stochastic regeneration. + + For controlling how resource regeneration behavior... + Regen Halfwidth: width of regen kernel = 1 + (2 * halfwidth); set >0 to create + spatial social dilemma + Regen Weight: regen probability per tile counted by the regen kernel + Max Health: how many resource units can populate a source block + + Args: + num_partitions_row (int): Number of height-wise partitions (controls #zones). + num_partitions_col (int): Number of width-wise partitions (controls #zones). + num_wood_zones (int): Number of zones where wood will appear. + num_stone_zones (int): Number of zones where stone will appear. + num_wood_and_stone_zones (int): Number of zones where both wood and stone will + appear. + planner_gets_spatial_obs (bool): Whether the planner agent receives spatial + observations from the world. + full_observability (bool): Whether the mobile agents' spatial observation + includes the full world view or is instead an egocentric view. + mobile_agent_observation_range (int): If not using full_observability, + the spatial range (on each side of the agent) that is visible in the + spatial observations. + wood_regen_halfwidth (int): Regen halfwidth for wood. + wood_regen_weight (float): Regen weight for wood. + wood_max_health (int): Max wood units per wood source tile. + stone_regen_halfwidth (int): Regen halfwidth for stone. + stone_regen_weight (float): Regen weight for stone. + stone_max_health (int): Max stone units per stone source tile. + starting_agent_coin (int, float): Amount of coin agents have at t=0. + isoelastic_eta (float): Parameter controlling the shape of agent utility + wrt coin endowment. + energy_cost (float): Coefficient for converting labor to negative utility. + energy_warmup_constant (float): Decay constant that controls the rate at which + the effective energy cost is annealed from 0 to energy_cost. Set to 0 + (default) to disable annealing, meaning that the effective energy cost is + always energy_cost. The units of the decay constant depend on the choice of + energy_warmup_method. + energy_warmup_method (str): How to schedule energy annealing (warmup). If + "decay" (default), use the number of completed episodes. If "auto", + use the number of timesteps where the average agent reward was positive. + planner_reward_type (str): The type of reward used for the planner. Options + are "coin_eq_times_productivity" (default), + "inv_income_weighted_coin_endowment", and "inv_income_weighted_utility". + mixing_weight_gini_vs_coin (float): Degree to which equality is ignored w/ + "coin_eq_times_productivity". Default is 0, which weights equality and + productivity equally. If set to 1, only productivity is rewarded. + """ + + name = "multi_zone/simple_wood_and_stone" + + def __init__( + self, + *args, + num_partitions_row=8, + num_partitions_col=8, + num_wood_zones=6, + num_stone_zones=6, + num_wood_and_stone_zones=4, + **kwargs + ): + self.num_partitions_row = num_partitions_row + self.num_partitions_col = num_partitions_col + self.zone_specs = { + "Wood": (0, num_wood_zones), + "Stone": (1, num_stone_zones), + "WoodStone": (2, num_wood_and_stone_zones), + } + + super().__init__(*args, **kwargs) + + def make_source_prob_maps(self): + """ + Make maps specifying how likely each location is to be assigned as a resource + source tile. + + Returns: + source_prob_maps (dict): Contains a source probability map for both + stone and wood. + """ + # determines initial world probability masses + zone_names = list(self.zone_specs.keys()) + zone_indices = [v[0] for _, v in self.zone_specs.items()] + num_zones_per_type = [self.zone_specs[k][1] for k in zone_names] + num_zones = sum(num_zones_per_type) + + num_partitions_row = self.num_partitions_row + num_partitions_col = self.num_partitions_col + num_regions = num_partitions_row * num_partitions_col + + assert num_regions >= num_zones + + # define regions and starting resource-zone-type + # (wood, stone, mixed) for each region + + # note: row, col index over regions + partition_size_row = int(np.ceil(self.world_size[0] / num_partitions_row)) + partition_size_col = int(np.ceil(self.world_size[1] / num_partitions_col)) + + # serialize regions in the world and give them an index. + # -1 means no source blocks in that region + grid_zone_indices = np.concatenate( + [ + np.repeat(zone_indices, num_zones_per_type), + np.array([-1] * (num_regions - num_zones)), + ] + ) + np.random.shuffle(grid_zone_indices) + grid_zone_indices = grid_zone_indices.reshape( + (num_partitions_row, num_partitions_col) + ) + + wood_prob = np.where( + np.logical_or( + np.equal(grid_zone_indices, self.zone_specs["Wood"][0]), + np.equal(grid_zone_indices, self.zone_specs["WoodStone"][0]), + ), + np.ones_like(grid_zone_indices), + np.zeros_like(grid_zone_indices), + ) + wood_prob = np.kron( + wood_prob, np.ones((partition_size_row, partition_size_col)) + ) + wood_prob = wood_prob[: self.world_size[0], : self.world_size[1]] + wood_prob = wood_prob / np.mean(wood_prob) + + try: + assert wood_prob.shape[0] == self.world_size[0] + assert wood_prob.shape[1] == self.world_size[1] + except AssertionError: + print("World not correct size!") + raise + + stone_prob = np.where( + np.logical_or( + np.equal(grid_zone_indices, self.zone_specs["Stone"][0]), + np.equal(grid_zone_indices, self.zone_specs["WoodStone"][0]), + ), + np.ones_like(grid_zone_indices), + np.zeros_like(grid_zone_indices), + ) + stone_prob = np.kron( + stone_prob, np.ones((partition_size_row, partition_size_col)) + ) + stone_prob = stone_prob[: self.world_size[0], : self.world_size[1]] + stone_prob = stone_prob / np.mean(stone_prob) + + try: + assert stone_prob.shape[0] == self.world_size[0] + assert stone_prob.shape[1] == self.world_size[1] + except AssertionError: + print("World not correct size!") + raise + + return { + "Wood": wood_prob * self.layout_specs["Wood"]["starting_coverage"], + "Stone": stone_prob * self.layout_specs["Wood"]["starting_coverage"], + } + + def reset_starting_layout(self): + """ + Reset the starting layout of the world. Modifies parent scenario method such + that, before doing the reset, it first remakes the source prob maps. + """ + self.source_prob_maps = self.make_source_prob_maps() + super().reset_starting_layout() + + +@scenario_registry.add +class Quadrant(Uniform): + """ + World containing wood and stone, with water creating 4 nearly closed-off quadrants. + Wood is concentrated along the left of the map, stone along the top, creating + different resource concentrations within each of the quadrants. + + For controlling how resource regeneration behavior... + Coverage: if fraction, target fraction of total tiles; if integer, target number + of tiles + Regen Halfwidth: width of regen kernel = 1 + (2 * halfwidth); set >0 to create + a spatial social dilemma + Regen Weight: regen probability per tile counted by the regen kernel + Max Health: how many resource units can populate a source block + Clumpiness: degree to which resources are spatially clustered + Gradient Steepness: degree to which stone/wood are restricted to the top/bottom + of the map + + Args: + planner_gets_spatial_obs (bool): Whether the planner agent receives spatial + observations from the world. + full_observability (bool): Whether the mobile agents' spatial observation + includes the full world view or is instead an egocentric view. + mobile_agent_observation_range (int): If not using full_observability, + the spatial range (on each side of the agent) that is visible in the + spatial observations. + starting_wood_coverage (int, float): Target coverage of wood at t=0. + wood_regen_halfwidth (int): Regen halfwidth for wood. + wood_regen_weight (float): Regen weight for wood. + wood_max_health (int): Max wood units per wood source tile. + wood_clumpiness (float): Degree of wood clumping. + starting_stone_coverage (int, float): Target coverage of stone at t=0. + stone_regen_halfwidth (int): Regen halfwidth for stone. + stone_regen_weight (float): Regen weight for stone. + stone_max_health (int): Max stone units per stone source tile. + stone_clumpiness (float): Degree of stone clumping. + gradient_steepness (int, float): How steeply source tile probability falls + off from the top/bottom of the map. + checker_source_blocks (bool): Whether to space source tiles in a "checker" + formation. + starting_agent_coin (int, float): Amount of coin agents have at t=0. + isoelastic_eta (float): Parameter controlling the shape of agent utility + wrt coin endowment. + energy_cost (float): Coefficient for converting labor to negative utility. + energy_warmup_constant (float): Decay constant that controls the rate at which + the effective energy cost is annealed from 0 to energy_cost. Set to 0 + (default) to disable annealing, meaning that the effective energy cost is + always energy_cost. The units of the decay constant depend on the choice of + energy_warmup_method. + energy_warmup_method (str): How to schedule energy annealing (warmup). If + "decay" (default), use the number of completed episodes. If "auto", + use the number of timesteps where the average agent reward was positive. + planner_reward_type (str): The type of reward used for the planner. Options + are "coin_eq_times_productivity" (default), + "inv_income_weighted_coin_endowment", and "inv_income_weighted_utility". + mixing_weight_gini_vs_coin (float): Degree to which equality is ignored w/ + "coin_eq_times_productivity". Default is 0, which weights equality and + productivity equally. If set to 1, only productivity is rewarded. + + """ + + name = "quadrant/simple_wood_and_stone" + required_entities = Uniform.required_entities + ["Water"] + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + width = self.world_size[1] + height = self.world_size[0] + + o0 = 0.2 + o1 = 0.35 + rN = (0.5 + np.arange(height)) / height + cN = (0.5 + np.arange(width)) / width + rSeg = ((rN < o0) + (rN > o1)) * ((rN < 1 - o1) + (rN > 1 - o0)) + cSeg = ((cN < o0) + (cN > o1)) * ((cN < 1 - o1) + (cN > 1 - o0)) + water = np.zeros((height, width)) + water[:, height // 2] = rSeg + water[width // 2, :] = cSeg + self._water = water + + for k, v in self.source_prob_maps.items(): + v = v * (1 - self._water) + v = v / np.sum(v) + self.source_prob_maps[k] = v + + def make_source_prob_maps(self): + """ + Make maps specifying how likely each location is to be assigned as a resource + source tile. + + Returns: + source_prob_maps (dict): Contains a source probability map for both + stone and wood + """ + width = self.world_size[1] + height = self.world_size[0] + + prob_gradient = np.arange(height)[:, None].repeat(width, axis=1) ** ( + self.gradient_steepness / 2 + ) + w_prob_gradient = prob_gradient[::-1] + + prob_gradient = np.arange(width)[None].repeat(height, axis=0) ** ( + self.gradient_steepness / 2 + ) + s_prob_gradient = prob_gradient[:, ::-1] + + prob_sum = s_prob_gradient + w_prob_gradient + + s_prob_gradient = prob_sum * s_prob_gradient + w_prob_gradient = prob_sum * w_prob_gradient + + s_prob_gradient = s_prob_gradient / np.sum(s_prob_gradient) + w_prob_gradient = w_prob_gradient / np.sum(w_prob_gradient) + + return {"Stone": s_prob_gradient, "Wood": w_prob_gradient} + + def reset_starting_layout(self): + """ + Reset the starting layout of the world. Modifies parent scenario method to + add water and remove resources from water locations. + """ + + # Generate the starting layout like normal + super().reset_starting_layout() + + width = self.world_size[1] + height = self.world_size[0] + + # Remove anything at the water line + for entity, state in self.world.maps.items(): + if isinstance(state, dict): + state["health"][:, height // 2] = 0 + state["health"][width // 2, :] = 0 + state["owner"][:, height // 2] = -1 + state["owner"][width // 2, :] = -1 + else: + state[:, height // 2] = 0 + state[width // 2, :] = 0 + self.world.maps.set(entity, state) + for entity, state in self.source_maps.items(): + state[:, height // 2] = 0 + state[width // 2, :] = 0 + self.source_maps[entity] = state + + # Place water + self.world.maps.set("Water", self._water) diff --git a/ai_economist/foundation/scenarios/simple_wood_and_stone/layout_from_file.py b/ai_economist/foundation/scenarios/simple_wood_and_stone/layout_from_file.py new file mode 100644 index 0000000..882f96f --- /dev/null +++ b/ai_economist/foundation/scenarios/simple_wood_and_stone/layout_from_file.py @@ -0,0 +1,800 @@ +# Copyright (c) 2020, salesforce.com, inc. +# All rights reserved. +# SPDX-License-Identifier: BSD-3-Clause +# For full license text, see the LICENSE file in the repo root +# or https://opensource.org/licenses/BSD-3-Clause + +from copy import deepcopy +from pathlib import Path + +import numpy as np +from scipy import signal + +from ai_economist.foundation.base.base_env import BaseEnvironment, scenario_registry +from ai_economist.foundation.scenarios.utils import rewards, social_metrics + + +@scenario_registry.add +class LayoutFromFile(BaseEnvironment): + """ + World containing stone and wood with stochastic regeneration. Refers to a fixed + layout file (see ./map_txt/ for examples) to determine the spatial arrangement of + stone, wood, and water tiles. + + Args: + planner_gets_spatial_obs (bool): Whether the planner agent receives spatial + observations from the world. + full_observability (bool): Whether the mobile agents' spatial observation + includes the full world view or is instead an egocentric view. + mobile_agent_observation_range (int): If not using full_observability, + the spatial range (on each side of the agent) that is visible in the + spatial observations. + env_layout_file (str): Name of the layout file in ./map_txt/ to use. + Note: The world dimensions of that layout must match the world dimensions + argument used to construct the environment. + resource_regen_prob (float): Probability that an empty source tile will + regenerate a new resource unit. + fixed_four_skill_and_loc (bool): Whether to use a fixed set of build skills and + starting locations, with agents grouped into starting locations based on + which skill quartile they are in. False, by default. + True, for experiments in https://arxiv.org/abs/2004.13332. + Note: Requires that the environment uses the "Build" component with + skill_dist="pareto". + starting_agent_coin (int, float): Amount of coin agents have at t=0. Defaults + to zero coin. + isoelastic_eta (float): Parameter controlling the shape of agent utility + wrt coin endowment. + energy_cost (float): Coefficient for converting labor to negative utility. + energy_warmup_constant (float): Decay constant that controls the rate at which + the effective energy cost is annealed from 0 to energy_cost. Set to 0 + (default) to disable annealing, meaning that the effective energy cost is + always energy_cost. The units of the decay constant depend on the choice of + energy_warmup_method. + energy_warmup_method (str): How to schedule energy annealing (warmup). If + "decay" (default), use the number of completed episodes. If "auto", + use the number of timesteps where the average agent reward was positive. + planner_reward_type (str): The type of reward used for the planner. Options + are "coin_eq_times_productivity" (default), + "inv_income_weighted_coin_endowment", and "inv_income_weighted_utility". + mixing_weight_gini_vs_coin (float): Degree to which equality is ignored w/ + "coin_eq_times_productivity". Default is 0, which weights equality and + productivity equally. If set to 1, only productivity is rewarded. + """ + + name = "layout_from_file/simple_wood_and_stone" + agent_subclasses = ["BasicMobileAgent", "BasicPlanner"] + required_entities = ["Wood", "Stone", "Water"] + + def __init__( + self, + *base_env_args, + planner_gets_spatial_info=True, + full_observability=False, + mobile_agent_observation_range=5, + env_layout_file="quadrant_25x25_20each_30clump.txt", + resource_regen_prob=0.01, + fixed_four_skill_and_loc=False, + starting_agent_coin=0, + isoelastic_eta=0.23, + energy_cost=0.21, + energy_warmup_constant=0, + energy_warmup_method="decay", + planner_reward_type="coin_eq_times_productivity", + mixing_weight_gini_vs_coin=0.0, + **base_env_kwargs, + ): + super().__init__(*base_env_args, **base_env_kwargs) + + # Whether agents receive spatial information in their observation tensor + self._planner_gets_spatial_info = bool(planner_gets_spatial_info) + + # Whether the (non-planner) agents can see the whole world map + self._full_observability = bool(full_observability) + + self._mobile_agent_observation_range = int(mobile_agent_observation_range) + + # Load in the layout + path_to_layout_file = Path(f"{Path(__file__).parent}/map_txt/{env_layout_file}") + + with open(path_to_layout_file, "r") as f: + self.env_layout_string = f.read() + self.env_layout = self.env_layout_string.split(";") + + # Convert the layout to landmark maps + landmark_lookup = {"W": "Wood", "S": "Stone", "@": "Water"} + self._source_maps = { + r: np.zeros(self.world_size) for r in landmark_lookup.values() + } + for r, symbol_row in enumerate(self.env_layout): + for c, symbol in enumerate(symbol_row): + landmark = landmark_lookup.get(symbol, None) + if landmark: + self._source_maps[landmark][r, c] = 1 + + # For controlling how resource regeneration behavior + self.layout_specs = dict( + Wood={ + "regen_weight": float(resource_regen_prob), + "regen_halfwidth": 0, + "max_health": 1, + }, + Stone={ + "regen_weight": float(resource_regen_prob), + "regen_halfwidth": 0, + "max_health": 1, + }, + ) + assert 0 <= self.layout_specs["Wood"]["regen_weight"] <= 1 + assert 0 <= self.layout_specs["Stone"]["regen_weight"] <= 1 + + # How much coin do agents begin with at upon reset + self.starting_agent_coin = float(starting_agent_coin) + assert self.starting_agent_coin >= 0.0 + + # Controls the diminishing marginal utility of coin. + # isoelastic_eta=0 means no diminishing utility. + self.isoelastic_eta = float(isoelastic_eta) + assert 0.0 <= self.isoelastic_eta <= 1.0 + + # The amount that labor is weighted in utility computation + # (once annealing is finished) + self.energy_cost = float(energy_cost) + assert self.energy_cost >= 0 + + # Which method to use for calculating the progress of energy annealing + # If method = 'decay': #completed episodes + # If method = 'auto' : #timesteps where avg. agent reward > 0 + self.energy_warmup_method = energy_warmup_method.lower() + assert self.energy_warmup_method in ["decay", "auto"] + # Decay constant for annealing to full energy cost + # (if energy_warmup_constant == 0, there is no annealing) + self.energy_warmup_constant = float(energy_warmup_constant) + assert self.energy_warmup_constant >= 0 + self._auto_warmup_integrator = 0 + + # Which social welfare function to use + self.planner_reward_type = str(planner_reward_type).lower() + + # How much to weight equality if using SWF=eq*prod: + # 0 -> SWF=eq * prod + # 1 -> SWF=prod + self.mixing_weight_gini_vs_coin = float(mixing_weight_gini_vs_coin) + assert 0 <= self.mixing_weight_gini_vs_coin <= 1.0 + + # Use this to calculate marginal changes and deliver that as reward + self.init_optimization_metric = {agent.idx: 0 for agent in self.all_agents} + self.prev_optimization_metric = {agent.idx: 0 for agent in self.all_agents} + self.curr_optimization_metric = {agent.idx: 0 for agent in self.all_agents} + + """ + Fixed Four Skill and Loc + ------------------------ + """ + self.agent_starting_pos = {agent.idx: [] for agent in self.world.agents} + + self.fixed_four_skill_and_loc = bool(fixed_four_skill_and_loc) + if self.fixed_four_skill_and_loc: + bm = self.get_component("Build") + assert bm.skill_dist == "pareto" + pmsm = bm.payment_max_skill_multiplier + + # Temporarily switch to a fixed seed for controlling randomness + seed_state = np.random.get_state() + np.random.seed(seed=1) + + # Generate a batch (100000) of num_agents (sorted/clipped) Pareto samples. + pareto_samples = np.random.pareto(4, size=(100000, self.n_agents)) + clipped_skills = np.minimum(pmsm, (pmsm - 1) * pareto_samples + 1) + sorted_clipped_skills = np.sort(clipped_skills, axis=1) + # The skill level of the i-th skill-ranked agent is the average of the + # i-th ranked samples throughout the batch. + average_ranked_skills = sorted_clipped_skills.mean(axis=0) + self._avg_ranked_skill = average_ranked_skills * bm.payment + + np.random.set_state(seed_state) + + # Fill in the starting location associated with each skill rank + starting_ranked_locs = [ + # Worst group of agents goes in top right + (0, self.world_size[1] - 1), + # Second-worst group of agents goes in bottom left + (self.world_size[0] - 1, 0), + # Second-best group of agents goes in top left + (0, 0), + # Best group of agents goes in bottom right + (self.world_size[1] - 1, self.world_size[1] - 1), + ] + self._ranked_locs = [] + + # Based on skill, assign each agent to one of the location groups + skill_groups = np.floor( + np.arange(self.n_agents) * (4 / self.n_agents), + ).astype(np.int) + n_in_group = np.zeros(4, dtype=np.int) + for g in skill_groups: + # The position within the group is given by the number of agents + # counted in the group thus far. + g_pos = n_in_group[g] + + # Top right + if g == 0: + r = starting_ranked_locs[g][0] + (g_pos // 4) + c = starting_ranked_locs[g][1] - (g_pos % 4) + self._ranked_locs.append((r, c)) + + # Bottom left + elif g == 1: + r = starting_ranked_locs[g][0] - (g_pos // 4) + c = starting_ranked_locs[g][1] + (g_pos % 4) + self._ranked_locs.append((r, c)) + + # Top left + elif g == 2: + r = starting_ranked_locs[g][0] + (g_pos // 4) + c = starting_ranked_locs[g][1] + (g_pos % 4) + self._ranked_locs.append((r, c)) + + # Bottom right + elif g == 3: + r = starting_ranked_locs[g][0] - (g_pos // 4) + c = starting_ranked_locs[g][1] - (g_pos % 4) + self._ranked_locs.append((r, c)) + + else: + raise ValueError + + # Count the agent we just placed. + n_in_group[g] = n_in_group[g] + 1 + + @property + def energy_weight(self): + """ + Energy annealing progress. Multiply with self.energy_cost to get the + effective energy coefficient. + """ + if self.energy_warmup_constant <= 0.0: + return 1.0 + + if self.energy_warmup_method == "decay": + return float(1.0 - np.exp(-self._completions / self.energy_warmup_constant)) + + if self.energy_warmup_method == "auto": + return float( + 1.0 + - np.exp(-self._auto_warmup_integrator / self.energy_warmup_constant) + ) + + raise NotImplementedError + + def get_current_optimization_metrics(self): + """ + Compute optimization metrics based on the current state. Used to compute reward. + + Returns: + curr_optimization_metric (dict): A dictionary of {agent.idx: metric} + with an entry for each agent (including the planner) in the env. + """ + curr_optimization_metric = {} + # (for agents) + for agent in self.world.agents: + curr_optimization_metric[agent.idx] = rewards.isoelastic_coin_minus_labor( + coin_endowment=agent.total_endowment("Coin"), + total_labor=agent.state["endogenous"]["Labor"], + isoelastic_eta=self.isoelastic_eta, + labor_coefficient=self.energy_weight * self.energy_cost, + ) + # (for the planner) + if self.planner_reward_type == "coin_eq_times_productivity": + curr_optimization_metric[ + self.world.planner.idx + ] = rewards.coin_eq_times_productivity( + coin_endowments=np.array( + [agent.total_endowment("Coin") for agent in self.world.agents] + ), + equality_weight=1 - self.mixing_weight_gini_vs_coin, + ) + elif self.planner_reward_type == "inv_income_weighted_coin_endowments": + curr_optimization_metric[ + self.world.planner.idx + ] = rewards.inv_income_weighted_coin_endowments( + coin_endowments=np.array( + [agent.total_endowment("Coin") for agent in self.world.agents] + ) + ) + elif self.planner_reward_type == "inv_income_weighted_utility": + curr_optimization_metric[ + self.world.planner.idx + ] = rewards.inv_income_weighted_utility( + coin_endowments=np.array( + [agent.total_endowment("Coin") for agent in self.world.agents] + ), + utilities=np.array( + [curr_optimization_metric[agent.idx] for agent in self.world.agents] + ), + ) + else: + print("No valid planner reward selected!") + raise NotImplementedError + return curr_optimization_metric + + # The following methods must be implemented for each scenario + # ----------------------------------------------------------- + + def reset_starting_layout(self): + """ + Part 1/2 of scenario reset. This method handles resetting the state of the + environment managed by the scenario (i.e. resource & landmark layout). + + Here, reset to the layout in the fixed layout file + """ + self.world.maps.clear() + for landmark, landmark_map in self._source_maps.items(): + self.world.maps.set(landmark, landmark_map) + if landmark in ["Stone", "Wood"]: + self.world.maps.set(landmark + "SourceBlock", landmark_map) + + def reset_agent_states(self): + """ + Part 2/2 of scenario reset. This method handles resetting the state of the + agents themselves (i.e. inventory, locations, etc.). + + Here, empty inventories and place mobile agents in random, accessible + locations to start. Note: If using fixed_four_skill_and_loc, the starting + locations will be overridden in self.additional_reset_steps. + """ + self.world.clear_agent_locs() + for agent in self.world.agents: + agent.state["inventory"] = {k: 0 for k in agent.inventory.keys()} + agent.state["escrow"] = {k: 0 for k in agent.inventory.keys()} + agent.state["endogenous"] = {k: 0 for k in agent.endogenous.keys()} + # Add starting coin + agent.state["inventory"]["Coin"] = float(self.starting_agent_coin) + + self.world.planner.state["inventory"] = { + k: 0 for k in self.world.planner.inventory.keys() + } + self.world.planner.state["escrow"] = { + k: 0 for k in self.world.planner.escrow.keys() + } + + for agent in self.world.agents: + r = np.random.randint(0, self.world_size[0]) + c = np.random.randint(0, self.world_size[1]) + n_tries = 0 + while not self.world.can_agent_occupy(r, c, agent): + r = np.random.randint(0, self.world_size[0]) + c = np.random.randint(0, self.world_size[1]) + n_tries += 1 + if n_tries > 200: + raise TimeoutError + r, c = self.world.set_agent_loc(agent, r, c) + + def scenario_step(self): + """ + Update the state of the world according to whatever rules this scenario + implements. + + This gets called in the 'step' method (of base_env) after going through each + component step and before generating observations, rewards, etc. + + In this class of scenarios, the scenario step handles stochastic resource + regeneration. + """ + + resources = ["Wood", "Stone"] + + for resource in resources: + d = 1 + (2 * self.layout_specs[resource]["regen_halfwidth"]) + kernel = ( + self.layout_specs[resource]["regen_weight"] * np.ones((d, d)) / (d ** 2) + ) + + resource_map = self.world.maps.get(resource) + resource_source_blocks = self.world.maps.get(resource + "SourceBlock") + spawnable = ( + self.world.maps.empty + resource_map + resource_source_blocks + ) > 0 + spawnable *= resource_source_blocks > 0 + + health = np.maximum(resource_map, resource_source_blocks) + respawn = np.random.rand(*health.shape) < signal.convolve2d( + health, kernel, "same" + ) + respawn *= spawnable + + self.world.maps.set( + resource, + np.minimum( + resource_map + respawn, self.layout_specs[resource]["max_health"] + ), + ) + + def generate_observations(self): + """ + Generate observations associated with this scenario. + + A scenario does not need to produce observations and can provide observations + for only some agent types; however, for a given agent type, it should either + always or never yield an observation. If it does yield an observation, + that observation should always have the same structure/sizes! + + Returns: + obs (dict): A dictionary of {agent.idx: agent_obs_dict}. In words, + return a dictionary with an entry for each agent (which can including + the planner) for which this scenario provides an observation. For each + entry, the key specifies the index of the agent and the value contains + its associated observation dictionary. + + Here, non-planner agents receive spatial observations (depending on the env + config) as well as the contents of their inventory and endogenous quantities. + The planner also receives spatial observations (again, depending on the env + config) as well as the inventory of each of the mobile agents. + """ + obs = {} + curr_map = self.world.maps.state + + owner_map = self.world.maps.owner_state + loc_map = self.world.loc_map + agent_idx_maps = np.concatenate([owner_map, loc_map[None, :, :]], axis=0) + agent_idx_maps += 2 + agent_idx_maps[agent_idx_maps == 1] = 0 + + agent_locs = { + str(agent.idx): { + "loc-row": agent.loc[0] / self.world_size[0], + "loc-col": agent.loc[1] / self.world_size[1], + } + for agent in self.world.agents + } + agent_invs = { + str(agent.idx): { + "inventory-" + k: v * self.inv_scale for k, v in agent.inventory.items() + } + for agent in self.world.agents + } + + obs[self.world.planner.idx] = { + "inventory-" + k: v * self.inv_scale + for k, v in self.world.planner.inventory.items() + } + if self._planner_gets_spatial_info: + obs[self.world.planner.idx].update( + dict(map=curr_map, idx_map=agent_idx_maps) + ) + + # Mobile agents see the full map. Convey location info via one-hot map channels. + if self._full_observability: + for agent in self.world.agents: + my_map = np.array(agent_idx_maps) + my_map[my_map == int(agent.idx) + 2] = 1 + sidx = str(agent.idx) + obs[sidx] = {"map": curr_map, "idx_map": my_map} + obs[sidx].update(agent_invs[sidx]) + + # Mobile agents only see within a window around their position + else: + w = ( + self._mobile_agent_observation_range + ) # View halfwidth (only applicable without full observability) + + padded_map = np.pad( + curr_map, + [(0, 1), (w, w), (w, w)], + mode="constant", + constant_values=[(0, 1), (0, 0), (0, 0)], + ) + + padded_idx = np.pad( + agent_idx_maps, + [(0, 0), (w, w), (w, w)], + mode="constant", + constant_values=[(0, 0), (0, 0), (0, 0)], + ) + + for agent in self.world.agents: + r, c = [c + w for c in agent.loc] + visible_map = padded_map[ + :, (r - w) : (r + w + 1), (c - w) : (c + w + 1) + ] + visible_idx = np.array( + padded_idx[:, (r - w) : (r + w + 1), (c - w) : (c + w + 1)] + ) + + visible_idx[visible_idx == int(agent.idx) + 2] = 1 + + sidx = str(agent.idx) + + obs[sidx] = {"map": visible_map, "idx_map": visible_idx} + obs[sidx].update(agent_locs[sidx]) + obs[sidx].update(agent_invs[sidx]) + + # Agent-wise planner info (gets crunched into the planner obs in the + # base scenario code) + obs["p" + sidx] = agent_invs[sidx] + if self._planner_gets_spatial_info: + obs["p" + sidx].update(agent_locs[sidx]) + + return obs + + def compute_reward(self): + """ + Apply the reward function(s) associated with this scenario to get the rewards + from this step. + + Returns: + rew (dict): A dictionary of {agent.idx: agent_obs_dict}. In words, + return a dictionary with an entry for each agent in the environment + (including the planner). For each entry, the key specifies the index of + the agent and the value contains the scalar reward earned this timestep. + + Rewards are computed as the marginal utility (agents) or marginal social + welfare (planner) experienced on this timestep. Ignoring discounting, + this means that agents' (planner's) objective is to maximize the utility + (social welfare) associated with the terminal state of the episode. + """ + + # "curr_optimization_metric" hasn't been updated yet, so it gives us the + # utility from the last step. + utility_at_end_of_last_time_step = deepcopy(self.curr_optimization_metric) + + # compute current objectives and store the values + self.curr_optimization_metric = self.get_current_optimization_metrics() + + # reward = curr - prev objectives + rew = { + k: float(v - utility_at_end_of_last_time_step[k]) + for k, v in self.curr_optimization_metric.items() + } + + # store the previous objective values + self.prev_optimization_metric.update(utility_at_end_of_last_time_step) + + # Automatic Energy Cost Annealing + # ------------------------------- + avg_agent_rew = np.mean([rew[a.idx] for a in self.world.agents]) + # Count the number of timesteps where the avg agent reward was > 0 + if avg_agent_rew > 0: + self._auto_warmup_integrator += 1 + + return rew + + # Optional methods for customization + # ---------------------------------- + + def additional_reset_steps(self): + """ + Extra scenario-specific steps that should be performed at the end of the reset + cycle. + + For each reset cycle... + First, reset_starting_layout() and reset_agent_states() will be called. + + Second, .reset() will be called for each registered component. + + Lastly, this method will be called to allow for any final customization of + the reset cycle. + + For this scenario, this method resets optimization metric trackers. If using + fixed_four_skill_and_loc, this is where each agent gets assigned to one of + the four fixed skill/loc combinations. The agent-->skill/loc assignment is + permuted so that all four skill/loc combinations are used. + """ + if self.fixed_four_skill_and_loc: + self.world.clear_agent_locs() + for i, agent in enumerate(self.world.get_random_order_agents()): + self.world.set_agent_loc(agent, *self._ranked_locs[i]) + agent.state["build_payment"] = self._avg_ranked_skill[i] + + # compute current objectives + curr_optimization_metric = self.get_current_optimization_metrics() + + self.curr_optimization_metric = deepcopy(curr_optimization_metric) + self.init_optimization_metric = deepcopy(curr_optimization_metric) + self.prev_optimization_metric = deepcopy(curr_optimization_metric) + + def scenario_metrics(self): + """ + Allows the scenario to generate metrics (collected along with component metrics + in the 'metrics' property). + + To have the scenario add metrics, this function needs to return a dictionary of + {metric_key: value} where 'value' is a scalar (no nesting or lists!) + + Here, summarize social metrics, endowments, utilities, and labor cost annealing. + """ + metrics = dict() + + coin_endowments = np.array( + [agent.total_endowment("Coin") for agent in self.world.agents] + ) + metrics["social/productivity"] = social_metrics.get_productivity( + coin_endowments + ) + metrics["social/equality"] = social_metrics.get_equality(coin_endowments) + + utilities = np.array( + [self.curr_optimization_metric[agent.idx] for agent in self.world.agents] + ) + metrics[ + "social_welfare/coin_eq_times_productivity" + ] = rewards.coin_eq_times_productivity( + coin_endowments=coin_endowments, equality_weight=1.0 + ) + metrics[ + "social_welfare/inv_income_weighted_coin_endow" + ] = rewards.inv_income_weighted_coin_endowments(coin_endowments=coin_endowments) + metrics[ + "social_welfare/inv_income_weighted_utility" + ] = rewards.inv_income_weighted_utility( + coin_endowments=coin_endowments, utilities=utilities + ) + + for agent in self.all_agents: + for resource, quantity in agent.inventory.items(): + metrics[ + "endow/{}/{}".format(agent.idx, resource) + ] = agent.total_endowment(resource) + + if agent.endogenous is not None: + for resource, quantity in agent.endogenous.items(): + metrics["endogenous/{}/{}".format(agent.idx, resource)] = quantity + + metrics["util/{}".format(agent.idx)] = self.curr_optimization_metric[ + agent.idx + ] + + # Labor weight + metrics["labor/weighted_cost"] = self.energy_cost * self.energy_weight + metrics["labor/warmup_integrator"] = int(self._auto_warmup_integrator) + + return metrics + + +@scenario_registry.add +class SplitLayout(LayoutFromFile): + """ + Extends layout_from_file/simple_wood_and_stone to impose a row of water midway + through the map, uses a fixed set of pareto-distributed building skills (requires a + Build component), and places agents in the top/bottom depending on skill rank. + + Args: + water_row (int): Row of the map where the water barrier is placed. Defaults + to half the world height. + skill_rank_of_top_agents (int, float, tuple, list): Index/indices specifying + which agent(s) to place in the top of the map. Indices refer to the skill + ranking, with 0 referring to the highest-skilled agent. Defaults to only + the highest-skilled agent in the top. + planner_gets_spatial_obs (bool): Whether the planner agent receives spatial + observations from the world. + full_observability (bool): Whether the mobile agents' spatial observation + includes the full world view or is instead an egocentric view. + mobile_agent_observation_range (int): If not using full_observability, + the spatial range (on each side of the agent) that is visible in the + spatial observations. + env_layout_file (str): Name of the layout file in ./map_txt/ to use. + Note: The world dimensions of that layout must match the world dimensions + argument used to construct the environment. + resource_regen_prob (float): Probability that an empty source tile will + regenerate a new resource unit. + starting_agent_coin (int, float): Amount of coin agents have at t=0. Defaults + to zero coin. + isoelastic_eta (float): Parameter controlling the shape of agent utility + wrt coin endowment. + energy_cost (float): Coefficient for converting labor to negative utility. + energy_warmup_constant (float): Decay constant that controls the rate at which + the effective energy cost is annealed from 0 to energy_cost. Set to 0 + (default) to disable annealing, meaning that the effective energy cost is + always energy_cost. The units of the decay constant depend on the choice of + energy_warmup_method. + energy_warmup_method (str): How to schedule energy annealing (warmup). If + "decay" (default), use the number of completed episodes. If "auto", + use the number of timesteps where the average agent reward was positive. + planner_reward_type (str): The type of reward used for the planner. Options + are "coin_eq_times_productivity" (default), + "inv_income_weighted_coin_endowment", and "inv_income_weighted_utility". + mixing_weight_gini_vs_coin (float): Degree to which equality is ignored w/ + "coin_eq_times_productivity". Default is 0, which weights equality and + productivity equally. If set to 1, only productivity is rewarded. + """ + + name = "split_layout/simple_wood_and_stone" + + def __init__( + self, + *args, + water_row=None, + skill_rank_of_top_agents=None, + **kwargs, + ): + super().__init__(*args, **kwargs) + + if self.fixed_four_skill_and_loc: + raise ValueError( + "The split layout scenario does not support " + "fixed_four_skill_and_loc. Set this to False." + ) + + # Augment the fixed layout to include a row of water through the middle + if water_row is None: + self._water_line = self.world_size[0] // 2 + else: + self._water_line = int(water_row) + assert 0 < self._water_line < self.world_size[0] - 1 + for landmark, landmark_map in self._source_maps.items(): + landmark_map[self._water_line, :] = 1 if landmark == "Water" else 0 + self._source_maps[landmark] = landmark_map + + # Controls logic for which agents (by skill rank) get placed on the top + if skill_rank_of_top_agents is None: + skill_rank_of_top_agents = [0] + + if isinstance(skill_rank_of_top_agents, (int, float)): + self.skill_rank_of_top_agents = [int(skill_rank_of_top_agents)] + elif isinstance(skill_rank_of_top_agents, (tuple, list)): + self.skill_rank_of_top_agents = list(set(skill_rank_of_top_agents)) + else: + raise TypeError( + "skill_rank_of_top_agents must be a scalar " + "index, or a list of scalar indices." + ) + for rank in self.skill_rank_of_top_agents: + assert 0 <= rank < self.n_agents + assert 0 < len(self.skill_rank_of_top_agents) < self.n_agents + + # Set the skill associated with each skill rank + bm = self.get_component("Build") + assert bm.skill_dist == "pareto" + pmsm = bm.payment_max_skill_multiplier + # Generate a batch (100000) of num_agents (sorted/clipped) Pareto samples. + pareto_samples = np.random.pareto(4, size=(100000, self.n_agents)) + clipped_skills = np.minimum(pmsm, (pmsm - 1) * pareto_samples + 1) + sorted_clipped_skills = np.sort(clipped_skills, axis=1) + # The skill level of the i-th skill-ranked agent is the average of the + # i-th ranked samples throughout the batch. + average_ranked_skills = sorted_clipped_skills.mean(axis=0) + self._avg_ranked_skill = average_ranked_skills * bm.payment + # Reverse the order so index 0 is the highest-skilled + self._avg_ranked_skill = self._avg_ranked_skill[::-1] + + def additional_reset_steps(self): + """ + Extra scenario-specific steps that should be performed at the end of the reset + cycle. + + For each reset cycle... + First, reset_starting_layout() and reset_agent_states() will be called. + + Second, .reset() will be called for each registered component. + + Lastly, this method will be called to allow for any final customization of + the reset cycle. + + For this scenario, this method resets optimization metric trackers. This is + where each agent gets assigned to one of the skills and the starting + locations are reset according to self.skill_rank_of_top_agents. + """ + self.world.clear_agent_locs() + for i, agent in enumerate(self.world.get_random_order_agents()): + agent.state["build_payment"] = self._avg_ranked_skill[i] + if i in self.skill_rank_of_top_agents: + r_min, r_max = 0, self._water_line + else: + r_min, r_max = self._water_line + 1, self.world_size[0] + + r = np.random.randint(r_min, r_max) + c = np.random.randint(0, self.world_size[1]) + n_tries = 0 + while not self.world.can_agent_occupy(r, c, agent): + r = np.random.randint(r_min, r_max) + c = np.random.randint(0, self.world_size[1]) + n_tries += 1 + if n_tries > 200: + raise TimeoutError + self.world.set_agent_loc(agent, r, c) + + # compute current objectives + curr_optimization_metric = self.get_current_optimization_metrics() + + self.curr_optimization_metric = deepcopy(curr_optimization_metric) + self.init_optimization_metric = deepcopy(curr_optimization_metric) + self.prev_optimization_metric = deepcopy(curr_optimization_metric) diff --git a/ai_economist/foundation/scenarios/simple_wood_and_stone/map_txt/closed_quadrant_25x25_20each_30clump.txt b/ai_economist/foundation/scenarios/simple_wood_and_stone/map_txt/closed_quadrant_25x25_20each_30clump.txt new file mode 100644 index 0000000..cfb2ebd --- /dev/null +++ b/ai_economist/foundation/scenarios/simple_wood_and_stone/map_txt/closed_quadrant_25x25_20each_30clump.txt @@ -0,0 +1 @@ +WWWWW W @ W ;WW W @ W W WW; W @ W W;SW S S @ ; @ W W ; SS @ ; S @ ; @ ; @ ;S S @ ; @ ; @ ;@@@@@@@@@@@@@@@@@@@@@@@@@; S @ ;S S @ ; S @ ;SS @ ; SS @ ;SS @ ; S @ ; @ ; @ ; @ ; @ ;S @ ; \ No newline at end of file diff --git a/ai_economist/foundation/scenarios/simple_wood_and_stone/map_txt/env-pure_and_mixed-15x15.txt b/ai_economist/foundation/scenarios/simple_wood_and_stone/map_txt/env-pure_and_mixed-15x15.txt new file mode 100644 index 0000000..b19ddf5 --- /dev/null +++ b/ai_economist/foundation/scenarios/simple_wood_and_stone/map_txt/env-pure_and_mixed-15x15.txt @@ -0,0 +1 @@ +WWW @ ;WSS @ ;WWW @ ;WWW @ ;WSS ;SWS ; @ ;@@@@ @@@@ @@@; @ ;WWW @ S ; WW SS ;WWW SS;W W @ ;W @ ; @ S ; \ No newline at end of file diff --git a/ai_economist/foundation/scenarios/simple_wood_and_stone/map_txt/env-pure_and_mixed-25x25.txt b/ai_economist/foundation/scenarios/simple_wood_and_stone/map_txt/env-pure_and_mixed-25x25.txt new file mode 100644 index 0000000..42e835f --- /dev/null +++ b/ai_economist/foundation/scenarios/simple_wood_and_stone/map_txt/env-pure_and_mixed-25x25.txt @@ -0,0 +1 @@ + WW W @ ; SWWW @ ;SSWW @ ;WSSSW @ ;WSSWW ;WS WS ; WWS ;SWW S @ ; S W @ ; WS W @ ; @ ; @ ;@@@@ @@@@@@@@@@@@ @@@; @ ; @ ; W @ SSSSS; WW @ SSS ; @ S SS; WW W @ SSS S;W WW SSSS ;WWW S SS; WWWW S S ;WW W @ S ; W @ S ; W @ S SSS; \ No newline at end of file diff --git a/ai_economist/foundation/scenarios/simple_wood_and_stone/map_txt/env-pure_and_mixed-40x40.txt b/ai_economist/foundation/scenarios/simple_wood_and_stone/map_txt/env-pure_and_mixed-40x40.txt new file mode 100644 index 0000000..fae4a20 --- /dev/null +++ b/ai_economist/foundation/scenarios/simple_wood_and_stone/map_txt/env-pure_and_mixed-40x40.txt @@ -0,0 +1 @@ + W WW ; W ; W W W ; W ; W W ; W W W ; W W ; WW ; ; ; ; ; ; ; ; ; SS W ; WW W; ; S S S; W WW S ; S S W ; W WS S S ; ; ; ; ; ; ; ; ; S ;S S ; ; SS ; ; S SS S ; SS S S ;S SS S S ;SSSS S SS ; \ No newline at end of file diff --git a/ai_economist/foundation/scenarios/simple_wood_and_stone/map_txt/quadrant_25x25_20each_30clump.txt b/ai_economist/foundation/scenarios/simple_wood_and_stone/map_txt/quadrant_25x25_20each_30clump.txt new file mode 100644 index 0000000..b178c73 --- /dev/null +++ b/ai_economist/foundation/scenarios/simple_wood_and_stone/map_txt/quadrant_25x25_20each_30clump.txt @@ -0,0 +1 @@ +WWWWW W @ W ;WW W @ W W WW; W @ W W;SW S S @ ; @ W W ; SS ; S ; ; ;S S @ ; @ ; @ ;@@@@@ @@@@@@@ @@@@@; S @ ;S S @ ; S @ ;SS ; SS ;SS ; S ; @ ; @ ; @ ; @ ;S @ ; \ No newline at end of file diff --git a/ai_economist/foundation/scenarios/simple_wood_and_stone/map_txt/quadrant_25x25_20each_30clump_no_water.txt b/ai_economist/foundation/scenarios/simple_wood_and_stone/map_txt/quadrant_25x25_20each_30clump_no_water.txt new file mode 100644 index 0000000..6bed29e --- /dev/null +++ b/ai_economist/foundation/scenarios/simple_wood_and_stone/map_txt/quadrant_25x25_20each_30clump_no_water.txt @@ -0,0 +1 @@ +WWWWW W W ;WW W W W WW; W W W;SW S S ; W W ; SS ; S ; ; ;S S ; ; ; ; S ;S S ; S ;SS ; SS ;SS ; S ; ; ; ; ;S ; \ No newline at end of file diff --git a/ai_economist/foundation/scenarios/simple_wood_and_stone/map_txt/quadrant_40x40_50each.txt b/ai_economist/foundation/scenarios/simple_wood_and_stone/map_txt/quadrant_40x40_50each.txt new file mode 100644 index 0000000..75277aa --- /dev/null +++ b/ai_economist/foundation/scenarios/simple_wood_and_stone/map_txt/quadrant_40x40_50each.txt @@ -0,0 +1 @@ +WWWWWWWW WW @@ WW ;WWWWWWWW WW @@ WW ;WWW W @@ WW W WWW; W @@ WW WW; W @@ WW WW;SSW S SS @@ ; @@ W W ; @@ W W ; SSS ; SSS ; S ; ; ; ;SS SS @@ ;SS SS @@ ; @@ ; @@ ; @@ ;@@@@@@@@ @@@@@@@@@@@@ @@@@@@@@;@@@@@@@@ @@@@@@@@@@@@ @@@@@@@@; S @@ ;SS SS @@ ;SS SS @@ ; S @@ ; S @@ ;SSS ; SSS ; SSS ;SSS ; SS ; SS ; @@ ; @@ ; @@ ; @@ ; @@ ; @@ ;SS @@ ;SS @@ ; \ No newline at end of file diff --git a/ai_economist/foundation/scenarios/simple_wood_and_stone/map_txt/quadrant_40x40_50each_no_water.txt b/ai_economist/foundation/scenarios/simple_wood_and_stone/map_txt/quadrant_40x40_50each_no_water.txt new file mode 100644 index 0000000..694cbc3 --- /dev/null +++ b/ai_economist/foundation/scenarios/simple_wood_and_stone/map_txt/quadrant_40x40_50each_no_water.txt @@ -0,0 +1 @@ +WWWWWWWW WW WW ;WWWWWWWW WW WW ;WWW W WW W WWW; W WW WW; W WW WW;SSW S SS ; W W ; W W ; SSS ; SSS ; S ; ; ; ;SS SS ;SS SS ; ; ; ; ; ; S ;SS SS ;SS SS ; S ; S ;SSS ; SSS ; SSS ;SSS ; SS ; SS ; ; ; ; ; ; ;SS ;SS ; \ No newline at end of file diff --git a/ai_economist/foundation/scenarios/simple_wood_and_stone/map_txt/quadrant_8x8_4each_8clump.txt b/ai_economist/foundation/scenarios/simple_wood_and_stone/map_txt/quadrant_8x8_4each_8clump.txt new file mode 100644 index 0000000..c8f9283 --- /dev/null +++ b/ai_economist/foundation/scenarios/simple_wood_and_stone/map_txt/quadrant_8x8_4each_8clump.txt @@ -0,0 +1 @@ +WWWW@WW W; WW @ WWW;SW S@ S ;S @ ;@@ @@@ @@; S @ ;S S @ ;S @ ; S@ ; diff --git a/ai_economist/foundation/scenarios/simple_wood_and_stone/map_txt/top_wood_bottom_stone_14x14.txt b/ai_economist/foundation/scenarios/simple_wood_and_stone/map_txt/top_wood_bottom_stone_14x14.txt new file mode 100644 index 0000000..eebe61d --- /dev/null +++ b/ai_economist/foundation/scenarios/simple_wood_and_stone/map_txt/top_wood_bottom_stone_14x14.txt @@ -0,0 +1 @@ + WW W ; A WW A ;WW WA W ; A A ; ; AA A ; A ; ; ; ; A ; A SS S;SA S AS;S SA SA \ No newline at end of file diff --git a/ai_economist/foundation/scenarios/simple_wood_and_stone/map_txt/uniform_25x25_25each_65clump.txt b/ai_economist/foundation/scenarios/simple_wood_and_stone/map_txt/uniform_25x25_25each_65clump.txt new file mode 100644 index 0000000..e9c8276 --- /dev/null +++ b/ai_economist/foundation/scenarios/simple_wood_and_stone/map_txt/uniform_25x25_25each_65clump.txt @@ -0,0 +1 @@ + SSSSS; SS SSSS; SS SSSS; S SSSS; SS ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; W; W WW; WW ;W WW W W WWW;WWWWW WW W WWWWW; \ No newline at end of file diff --git a/ai_economist/foundation/scenarios/utils/__init__.py b/ai_economist/foundation/scenarios/utils/__init__.py new file mode 100644 index 0000000..fc6cee4 --- /dev/null +++ b/ai_economist/foundation/scenarios/utils/__init__.py @@ -0,0 +1,5 @@ +# Copyright (c) 2020, salesforce.com, inc. +# All rights reserved. +# SPDX-License-Identifier: BSD-3-Clause +# For full license text, see the LICENSE file in the repo root +# or https://opensource.org/licenses/BSD-3-Clause diff --git a/ai_economist/foundation/scenarios/utils/rewards.py b/ai_economist/foundation/scenarios/utils/rewards.py new file mode 100644 index 0000000..7b4db8e --- /dev/null +++ b/ai_economist/foundation/scenarios/utils/rewards.py @@ -0,0 +1,133 @@ +# Copyright (c) 2020, salesforce.com, inc. +# All rights reserved. +# SPDX-License-Identifier: BSD-3-Clause +# For full license text, see the LICENSE file in the repo root +# or https://opensource.org/licenses/BSD-3-Clause + +import numpy as np + +from ai_economist.foundation.scenarios.utils import social_metrics + + +def isoelastic_coin_minus_labor( + coin_endowment, total_labor, isoelastic_eta, labor_coefficient +): + """Agent utility, concave increasing in coin and linearly decreasing in labor. + + Args: + coin_endowment (float, ndarray): The amount of coin owned by the agent(s). + total_labor (float, ndarray): The amount of labor performed by the agent(s). + isoelastic_eta (float): Constant describing the shape of the utility profile + with respect to coin endowment. Must be between 0 and 1. 0 yields utility + that increases linearly with coin. 1 yields utility that increases with + log(coin). Utility from coin uses: + https://en.wikipedia.org/wiki/Isoelastic_utility + labor_coefficient (float): Constant describing the disutility experienced per + unit of labor performed. Disutility from labor equals: + labor_coefficient * total_labor + + Returns: + Agent utility (float) or utilities (ndarray). + """ + # https://en.wikipedia.org/wiki/Isoelastic_utility + assert np.all(coin_endowment >= 0) + assert 0 <= isoelastic_eta <= 1.0 + + # Utility from coin endowment + if isoelastic_eta == 1.0: # dangerous + util_c = np.log(np.max(1, coin_endowment)) + else: # isoelastic_eta >= 0 + util_c = (coin_endowment ** (1 - isoelastic_eta) - 1) / (1 - isoelastic_eta) + + # disutility from labor + util_l = total_labor * labor_coefficient + + # Net utility + util = util_c - util_l + + return util + + +def coin_minus_labor_cost( + coin_endowment, total_labor, labor_exponent, labor_coefficient +): + """Agent utility, linearly increasing in coin and decreasing as a power of labor. + + Args: + coin_endowment (float, ndarray): The amount of coin owned by the agent(s). + total_labor (float, ndarray): The amount of labor performed by the agent(s). + labor_exponent (float): Constant describing the shape of the utility profile + with respect to total labor. Must be between >1. + labor_coefficient (float): Constant describing the disutility experienced per + unit of labor performed. Disutility from labor equals: + labor_coefficient * total_labor. + + Returns: + Agent utility (float) or utilities (ndarray). + """ + # https://en.wikipedia.org/wiki/Isoelastic_utility + assert np.all(coin_endowment >= 0) + assert labor_exponent > 1 + + # Utility from coin endowment + util_c = coin_endowment + + # Disutility from labor + util_l = (total_labor ** labor_exponent) * labor_coefficient + + # Net utility + util = util_c - util_l + + return util + + +def coin_eq_times_productivity(coin_endowments, equality_weight): + """Social welfare, measured as productivity scaled by the degree of coin equality. + + Args: + coin_endowments (ndarray): The array of coin endowments for each of the + agents in the simulated economy. + equality_weight (float): Constant that determines how productivity is scaled + by coin equality. Must be between 0 (SW = prod) and 1 (SW = prod * eq). + + Returns: + Product of coin equality and productivity (float). + """ + n_agents = len(coin_endowments) + prod = social_metrics.get_productivity(coin_endowments) / n_agents + equality = equality_weight * social_metrics.get_equality(coin_endowments) + ( + 1 - equality_weight + ) + return equality * prod + + +def inv_income_weighted_coin_endowments(coin_endowments): + """Social welfare, as weighted average endowment (weighted by inverse endowment). + + Args: + coin_endowments (ndarray): The array of coin endowments for each of the + agents in the simulated economy. + + Returns: + Weighted average coin endowment (float). + """ + pareto_weights = 1 / np.maximum(coin_endowments, 1) + pareto_weights = pareto_weights / np.sum(pareto_weights) + return np.sum(coin_endowments * pareto_weights) + + +def inv_income_weighted_utility(coin_endowments, utilities): + """Social welfare, as weighted average utility (weighted by inverse endowment). + + Args: + coin_endowments (ndarray): The array of coin endowments for each of the + agents in the simulated economy. + utilities (ndarray): The array of utilities for each of the agents in the + simulated economy. + + Returns: + Weighted average utility (float). + """ + pareto_weights = 1 / np.maximum(coin_endowments, 1) + pareto_weights = pareto_weights / np.sum(pareto_weights) + return np.sum(utilities * pareto_weights) diff --git a/ai_economist/foundation/scenarios/utils/social_metrics.py b/ai_economist/foundation/scenarios/utils/social_metrics.py new file mode 100644 index 0000000..3326e2e --- /dev/null +++ b/ai_economist/foundation/scenarios/utils/social_metrics.py @@ -0,0 +1,75 @@ +# Copyright (c) 2020, salesforce.com, inc. +# All rights reserved. +# SPDX-License-Identifier: BSD-3-Clause +# For full license text, see the LICENSE file in the repo root +# or https://opensource.org/licenses/BSD-3-Clause + +import numpy as np + + +def get_gini(endowments): + """Returns the normalized Gini index describing the distribution of endowments. + + https://en.wikipedia.org/wiki/Gini_coefficient + + Args: + endowments (ndarray): The array of endowments for each of the agents in the + simulated economy. + + Returns: + Normalized Gini index for the distribution of endowments (float). A value of 1 + indicates everything belongs to 1 agent (perfect inequality), whereas a + value of 0 indicates all agents have equal endowments (perfect equality). + + Note: + Uses a slightly different method depending on the number of agents. For fewer + agents (<30), uses an exact but slow method. Switches to using a much faster + method for more agents, where both methods produce approximately equivalent + results. + """ + n_agents = len(endowments) + + if n_agents < 30: # Slower. Accurate for all n. + diff_ij = np.abs( + endowments.reshape((n_agents, 1)) - endowments.reshape((1, n_agents)) + ) + diff = np.sum(diff_ij) + norm = 2 * n_agents * endowments.sum(axis=0) + unscaled_gini = diff / (norm + 1e-10) + gini = unscaled_gini / ((n_agents - 1) / n_agents) + return gini + + # Much faster. Slightly overestimated for low n. + s_endows = np.sort(endowments) + return 1 - (2 / (n_agents + 1)) * np.sum( + np.cumsum(s_endows) / (np.sum(s_endows) + 1e-10) + ) + + +def get_equality(endowments): + """Returns the complement of the normalized Gini index (equality = 1 - Gini). + + Args: + endowments (ndarray): The array of endowments for each of the agents in the + simulated economy. + + Returns: + Normalized equality index for the distribution of endowments (float). A value + of 0 indicates everything belongs to 1 agent (perfect inequality), + whereas a value of 1 indicates all agents have equal endowments (perfect + equality). + """ + return 1 - get_gini(endowments) + + +def get_productivity(coin_endowments): + """Returns the total coin inside the simulated economy. + + Args: + coin_endowments (ndarray): The array of coin endowments for each of the + agents in the simulated economy. + + Returns: + Total coin endowment (float). + """ + return np.sum(coin_endowments) diff --git a/ai_economist/foundation/utils.py b/ai_economist/foundation/utils.py new file mode 100644 index 0000000..85e12f1 --- /dev/null +++ b/ai_economist/foundation/utils.py @@ -0,0 +1,123 @@ +# Copyright (c) 2020, salesforce.com, inc. +# All rights reserved. +# SPDX-License-Identifier: BSD-3-Clause +# For full license text, see the LICENSE file in the repo root +# or https://opensource.org/licenses/BSD-3-Clause + +import json +import os +import sys +from hashlib import sha512 + +import lz4.frame +from Crypto.PublicKey import RSA + +from ai_economist.foundation.base.base_env import BaseEnvironment + + +def save_episode_log(game_object, filepath, compression_level=16): + """Save a lz4 compressed version of the dense log stored + in the provided game object""" + assert isinstance(game_object, BaseEnvironment) + compression_level = int(compression_level) + if compression_level < 0: + compression_level = 0 + elif compression_level > 16: + compression_level = 16 + + with lz4.frame.open( + filepath, mode="wb", compression_level=compression_level + ) as log_file: + log_bytes = bytes( + json.dumps( + game_object.previous_episode_dense_log, ensure_ascii=False + ).encode("utf-8") + ) + log_file.write(log_bytes) + + +def load_episode_log(filepath): + """Load the dense log saved at provided filepath""" + with lz4.frame.open(filepath, mode="rb") as log_file: + log_bytes = log_file.read() + return json.loads(log_bytes) + + +def verify_activation_code(): + """ + Validate the user's activation code. + If the activation code is valid, also save it in a text file for future reference. + If the activation code is invalid, simply exit the program + """ + path_to_activation_code_dir = os.path.dirname(os.path.abspath(__file__)) + + def validate_activation_code(code, msg=b"covid19 code activation"): + filepath = os.path.abspath( + os.path.join( + path_to_activation_code_dir, + "scenarios/covid19/key_to_check_activation_code_against", + ) + ) + with open(filepath, "r") as fp: + key_pair = RSA.import_key(fp.read()) + + hashed_msg = int.from_bytes(sha512(msg).digest(), byteorder="big") + signature = pow(hashed_msg, key_pair.d, key_pair.n) + try: + exp_from_code = int(code, 16) + hashed_msg_from_signature = pow(signature, exp_from_code, key_pair.n) + + return hashed_msg == hashed_msg_from_signature + except ValueError: + return False + + activation_code_filename = "activation_code.txt" + + filepath = os.path.join(path_to_activation_code_dir, activation_code_filename) + if activation_code_filename in os.listdir(path_to_activation_code_dir): + print("Using the activation code already present in '{}'".format(filepath)) + with open(filepath, "r") as fp: + activation_code = fp.read() + fp.close() + if validate_activation_code(activation_code): + return # already activated + print( + "The activation code saved in '{}' is incorrect! " + "Please correct the activation code and try again.".format(filepath) + ) + sys.exit(0) + else: + print( + "In order to run this simulation, you will need an activation code.\n" + "Please fill out the form at " + "https://forms.gle/dJ2gKDBqLDko1g7m7 and we will send you an " + "activation code to the provided email address.\n" + ) + num_attempts = 5 + attempt_num = 0 + while attempt_num < num_attempts: + activation_code = input( + f"Whenever you are ready, " + "please enter the activation code: " + f"(attempt {attempt_num + 1} / {num_attempts})" + ) + attempt_num += 1 + if validate_activation_code(activation_code): + print( + "Saving the activation code in '{}' for future " + "use.".format(filepath) + ) + with open( + os.path.join(path_to_activation_code_dir, activation_code_filename), + "w", + ) as fp: + fp.write(activation_code) + fp.close() + return + print("Incorrect activation code. Please try again.") + print( + "You have had {} attempts to provide the activate code. Unfortunately, " + "none of the activation code(s) you provided could be validated. " + "Exiting...".format(num_attempts) + ) + sys.exit(0) diff --git a/ai_economist/real_business_cycle/README.md b/ai_economist/real_business_cycle/README.md new file mode 100644 index 0000000..844b33b --- /dev/null +++ b/ai_economist/real_business_cycle/README.md @@ -0,0 +1,120 @@ +# Real Business Cycle (RBC) +This directory implements a **Real-Business-Cycle** (RBC) simulation with many heterogeneous, interacting strategic agents of various types, such as **consumers, firms, and the government**. For details, please refer to this paper "Finding General Equilibria in Many-Agent Economic Simulations using Deep Reinforcement Learning (ArXiv link forthcoming)". We also provide training code that uses deep multi-agent reinforcement learning to determine optimal economic policies and dynamics in these many agent environments. Below are instructions required to launch the training runs. + +**Note: The experiments require a GPU to run!** + +## Dependencies + +- torch>=1.9.0 +- pycuda==2021.1 +- matplotlib==3.2.1 + +## Running Local Jobs +To run a hyperparameter sweep of jobs on a local machine, use (see file for command line arguments and hyperparameter sweep dictionaries) + +``` +python train_multi_exps.py +``` + +## Configuration Dictionaries + +Configuration dictionaries are currently specified in Python code, and then written as `hparams.yaml` in the job directory. For examples, see the file `constants.py`. The dictionaries contain "agents", "world", and "train" dictionaries which contain various hyperparameters. + +## Hyperparameter Sweeps + +The files `train_multi_exps.py` allow hyperparameter sweeps. These are specified in `*_param_sweeps` dictionaries in the file. For each hyperparameter, specify a list of one or more choices. The Cartesian product of all choices will be used. + +## Approximate Best Response Training + +To run a single approximate best-response (BR) training job on checkpoint policies, run `python train_bestresponse.py ROLLOUT_DIR NUM_EPISODES_TO_TRAIN --ep-strs ep1 ep2 --agent-type all`. The `--ep-strs` argument specifies which episodes to run on (for example, policies from episode 0, 10000, and 200000). These must be episodes for which policies were saved. It is possible to specify a single agent type. + + +## What Will Be Saved? + +A large amount of data will be saved -- one can set hyperparamter `train.save_dense_every` in the configuration dictionary (`hparams.yaml`/`constants.py`) to reduce this. + +At the top level, an experiment directory stores the results of many runs in a hyperparameter sweep. Example structure: + +``` +experiment/experimentname/ + rollout-999999-99999/ + brconsumer/ + ... + brfirm/ + episode_XXXX_consumer.npz + episode_XXXX_government.npz + episode_XXXX_firm.npz + saved_models/ + consumer_policy_XXX.pt + firm_policy_XXX.pt + government_policy_XXX.pt. + brgovernment/ + ... + hparams.yaml + action_arrays.pickle + episode_XXXX_consumer.npz + episode_XXXX_government.npz + episode_XXXX_firm.npz + saved_models/ + consumer_policy_XXX.pt + firm_policy_XXX.pt + government_policy_XXX.pt. + + rollout-777777-77777/ + ... +``` + +Files: + +`rollout-XXXXXX-XXX`: subdirectory containing all output for a single run. + +`hparams.yaml`: configuration dictionary with hyperparameters + +`action_arrays.pickle`: contains saved action arrays (allowing mapping action indices to the actual action, e.g. index 1 is price 1000.0, etc.) + +`episode_XXXX_AGENTTYPE.npz`: Contains dense rollouts stored as the output of a numpy.savez call. When loaded, can be treated like a dictionary of numpy arrays. Has keys: `['states', 'actions', 'rewards', 'action_array', 'aux_array']` (view keys by using `.files`). `states`, `actions`, `rewards`, and `aux_array` all refer to saved copies of CUDA arrays (described below). `action_array` is a small array mapping action indices to the actual action. + +`saved_models/AGENTTYPE_policy_XXX.pt`: a saved PyTorch state dict of the policy network, after episode XXX. + +## Structure Of Arrays + +`states` for any given agent type is an array storing observed states. It has shape `batch_size, ep_length, num_agents, agent_total_state_dim`. + +`actions` is an array consisting of the action _indices_ (integers). For firms and government, it is of shape `batch_size, ep_length, num_agents`. For consumers, it is of shape `batch_size, ep_length, num_agents, num_action_heads`. + +`rewards` stores total rewards, and is of shape `batch_size, ep_length, num_agents`. + +The `aux_array` stores additional information and may differ per agent type. The consumer `aux_array` stores _actual_ consumption of each firm's good (as opposed to attempted consumption). The firm `aux_array` stores the amount bought by the export market. + +## State Array Layout: + +States observed by each agent consist of a global state, plus additional state dimensions per agent. + +Global state: total dimension 4 * num_firms + 2 + 1 +- prices: 1 per firm +- wages: 1 per firm +- inventories: 1 per firm +- overdemanded flag: 1 per firm +- time + +Consumer additional state variables: total dimension global state + 2 +- budget +- theta + +Firm additional state variables: total dimension global state + 3 + num_firms +- budget +- capital +- production alpha +- one-hot representation identifying which firm + +## What Gets Loaded And Written By BR Code? + +The best response code loads in the `hparams.yaml` file, and the policies at a given time step (i.e. `saved_models/...policy_XXX.pt`). It then trains one of the policies while keeping the others fixed. Results are written to directories `brfirm`, `brconsumer`, `brgovernment` and contain dense rollouts and saved policy checkpoints, but from the best response training. + +## Which Hyperparameters Are Managed And Where? + +Initial values of state variables (budgets, initial wages, levels of capital, and so on) are set by the code in the method `__init_cuda_data_structs`. Some of these can be controlled from the hyperparameter dict; others are currently hardcoded. + +Other hyperparameters are specified in the configuration dictionary. + +Finally, the technology parameter (A) of the production function is currently hardcoded in the function call in `rbc/cuda/firm_rbc.cu`. diff --git a/ai_economist/real_business_cycle/experiment_utils.py b/ai_economist/real_business_cycle/experiment_utils.py new file mode 100644 index 0000000..6a7c948 --- /dev/null +++ b/ai_economist/real_business_cycle/experiment_utils.py @@ -0,0 +1,242 @@ +# Copyright (c) 2021, salesforce.com, inc. +# All rights reserved. +# SPDX-License-Identifier: BSD-3-Clause +# For full license text, see the LICENSE file in the repo root +# or https://opensource.org/licenses/BSD-3-Clause + +import hashlib +import itertools +import json +import os +import pickle +import struct +import time +from copy import deepcopy +from pathlib import Path + +import numpy as np +import yaml + +# defaults +_NUM_FIRMS = 10 + + +def _bigint_from_bytes(num_bytes): + """ + See https://github.com/openai/gym/blob/master/gym/utils/seeding.py. + """ + sizeof_int = 4 + padding = sizeof_int - len(num_bytes) % sizeof_int + num_bytes += b"\0" * padding + int_count = int(len(num_bytes) / sizeof_int) + unpacked = struct.unpack("{}I".format(int_count), num_bytes) + accum = 0 + for i, val in enumerate(unpacked): + accum += 2 ** (sizeof_int * 8 * i) * val + return accum + + +def seed_from_base_seed(base_seed): + """ + Hash base seed to reduce correlation. + """ + max_bytes = 4 + hash_func = hashlib.sha512(str(base_seed).encode("utf8")).digest() + + return _bigint_from_bytes(hash_func[:max_bytes]) + + +def hash_from_dict(d): + d_copy = deepcopy(d) + del (d_copy["train"])["base_seed"] + d_string = json.dumps(d_copy, sort_keys=True) + return int(hashlib.sha256(d_string.encode("utf8")).hexdigest()[:8], 16) + + +def cfg_dict_from_yaml( + hparams_path, + consumption_choices, + work_choices, + price_and_wage, + tax_choices, + group_name=None, +): + with open(hparams_path) as f: + d = yaml.safe_load(f) + + if group_name is not None: + d["metadata"]["group_name"] = group_name + d["metadata"]["hparamhash"] = hash_from_dict(d) + d["agents"][ + "consumer_consumption_actions_array" + ] = consumption_choices # Note: hardcoded + d["agents"]["consumer_work_actions_array"] = work_choices # Note: hardcoded + d["agents"]["firm_actions_array"] = price_and_wage # Note: hardcoded + d["agents"]["government_actions_array"] = tax_choices + d["train"]["save_dir"] = str(hparams_path.absolute().parent) + d["train"]["seed"] = seed_from_base_seed(d["train"]["base_seed"]) + return d + + +def run_experiment_batch_parallel( + experiment_dir, + consumption_choices, + work_choices, + price_and_wage, + tax_choices, + group_name=None, + consumers_only=False, + no_firms=False, + default_firm_action=None, + default_government_action=None, +): + hparams_path = Path(experiment_dir) / Path("hparams.yaml") + hparams_dict = cfg_dict_from_yaml( + hparams_path, + consumption_choices, + work_choices, + price_and_wage, + tax_choices, + group_name=group_name, + ) + print(f"hparams_dict {hparams_dict}") + # import this here so rest of file still imports without cuda installed + from rbc.cuda_manager import ConsumerFirmRunManagerBatchParallel + + if consumers_only: + m = ConsumerFirmRunManagerBatchParallel( + hparams_dict, + freeze_firms=default_firm_action, + freeze_govt=default_government_action, + ) + elif no_firms: + m = ConsumerFirmRunManagerBatchParallel( + hparams_dict, + freeze_firms=default_firm_action, + ) + else: + m = ConsumerFirmRunManagerBatchParallel(hparams_dict) + m.train() + + +def compare_global_states_within_type(states, global_state_size): + # every agent within a batch should have the same global state + first_agent_global = states[:, :, :1, :global_state_size] + all_agents_global = states[:, :, :, :global_state_size] + return np.isclose(all_agents_global, first_agent_global).all() + + +def compare_global_states_across_types( + consumer_states, firm_states, government_states, global_state_size +): + first_agent_global = consumer_states[:, :, :1, :global_state_size] + return ( + np.isclose(firm_states[:, :, :, :global_state_size], first_agent_global).all(), + np.isclose( + government_states[:, :, :, :global_state_size], first_agent_global + ).all(), + np.isclose( + consumer_states[:, :, :, :global_state_size], first_agent_global + ).all(), + ) + + +def check_no_negative_stocks(state, stock_offset, stock_size): + stocks = state[:, :, :, stock_offset : (stock_offset + stock_size)] + return (stocks >= -1.0e-3).all() + + +train_param_sweeps = { + "lr": [0.005, 0.001], + "entropy": [0.01], + "base_seed": [2596], + "batch_size": [64], + "clip_grad_norm": [1.0, 2.0, 5.0], +} + +# Other param sweeps +agent_param_sweeps = { + # "consumer_noponzi_eta": [0.1,0.05] +} + +world_param_sweeps = { + # "interest_rate": [0.02, 0.0] +} + + +def add_all(d, keys_list, target_val): + for k in keys_list: + d[k] = target_val + + +def sweep_cfg_generator( + base_cfg, + tr_param_sweeps=None, + ag_param_sweeps=None, + wld_param_sweeps=None, + seed_from_timestamp=False, + group_name=None, +): + # train_param_sweeps + if tr_param_sweeps is None: + tr_param_sweeps = {} + # agent_param_sweeps + if ag_param_sweeps is None: + ag_param_sweeps = {} + # world_param_sweeps + if wld_param_sweeps is None: + wld_param_sweeps = {} + + assert isinstance(tr_param_sweeps, dict) + assert isinstance(ag_param_sweeps, dict) + assert isinstance(wld_param_sweeps, dict) + + key_dict = {} # tells which key goes to which dict, e.g. "lr" -> "train", etc. + if len(tr_param_sweeps) > 0: + train_k, train_v = zip(*tr_param_sweeps.items()) + else: + train_k, train_v = (), () + add_all(key_dict, train_k, "train") + if len(ag_param_sweeps) > 0: + agent_k, agent_v = zip(*ag_param_sweeps.items()) + else: + agent_k, agent_v = (), () + add_all(key_dict, agent_k, "agents") + if len(wld_param_sweeps) > 0: + world_k, world_v = zip(*wld_param_sweeps.items()) + else: + world_k, world_v = (), () + add_all(key_dict, world_k, "world") + + k = train_k + agent_k + world_k + v = train_v + agent_v + world_v + + # have a "reverse lookup" dictionary for each key name + for combination in itertools.product(*v): + values_to_substitute = dict(zip(k, combination)) + out = deepcopy(base_cfg) + for key, value in values_to_substitute.items(): + out[key_dict[key]][key] = value + if seed_from_timestamp: + int_timestamp = int( + time.time() * 1000 + ) # time.time() returns float, multiply 1000 for higher resolution + out["train"]["base_seed"] += int_timestamp + if group_name is not None: + out["metadata"]["group"] = group_name + yield out + + +def create_job_dir(experiment_dir, job_name_base, cfg=None, action_arrays=None): + unique_id = time.time() + dirname = f"{job_name_base}-{unique_id}".replace(".", "-") + dir_path = Path(experiment_dir) / Path(dirname) + os.makedirs(str(dir_path), exist_ok=True) + cfg["metadata"]["dirname"] = dirname + cfg["metadata"]["group"] = str(Path(experiment_dir).name) + with open(dir_path / Path("hparams.yaml"), "w") as f: + f.write(yaml.dump(cfg)) + + if action_arrays is not None: + with open(dir_path / Path("action_arrays.pickle"), "wb") as f: + pickle.dump(action_arrays, f) diff --git a/ai_economist/real_business_cycle/rbc/__init__.py b/ai_economist/real_business_cycle/rbc/__init__.py new file mode 100644 index 0000000..fc6cee4 --- /dev/null +++ b/ai_economist/real_business_cycle/rbc/__init__.py @@ -0,0 +1,5 @@ +# Copyright (c) 2020, salesforce.com, inc. +# All rights reserved. +# SPDX-License-Identifier: BSD-3-Clause +# For full license text, see the LICENSE file in the repo root +# or https://opensource.org/licenses/BSD-3-Clause diff --git a/ai_economist/real_business_cycle/rbc/constants.py b/ai_economist/real_business_cycle/rbc/constants.py new file mode 100644 index 0000000..9db6d76 --- /dev/null +++ b/ai_economist/real_business_cycle/rbc/constants.py @@ -0,0 +1,638 @@ +# Copyright (c) 2021, salesforce.com, inc. +# All rights reserved. +# SPDX-License-Identifier: BSD-3-Clause +# For full license text, see the LICENSE file in the repo root +# or https://opensource.org/licenses/BSD-3-Clause + +import itertools + +import numpy as np +import torch + +_NP_DTYPE = np.float32 + + +def all_agents_export_experiment_template( + NUMFIRMS, NUMCONSUMERS, NUMGOVERNMENTS, episodes_const=30000 +): + consumption_choices = [ + np.array([0.0 + 1.0 * c for c in range(11)], dtype=_NP_DTYPE) + ] + work_choices = [ + np.array([0.0 + 20 * 13 * h for h in range(5)], dtype=_NP_DTYPE) + ] # specify dtype -- be consistent? + + consumption_choices = np.array( + list(itertools.product(*consumption_choices)), dtype=_NP_DTYPE + ) + work_choices = np.array(list(itertools.product(*work_choices)), dtype=_NP_DTYPE) + + price_choices = np.array([0.0 + 500.0 * c for c in range(6)], dtype=_NP_DTYPE) + wage_choices = np.array([0.0, 11.0, 22.0, 33.0, 44.0], dtype=_NP_DTYPE) + capital_choices = np.array([0.1], dtype=_NP_DTYPE) + price_and_wage = np.array( + list(itertools.product(price_choices, wage_choices, capital_choices)), + dtype=_NP_DTYPE, + ) + + # government action discretization + income_taxation_choices = np.array( + [0.0 + 0.2 * c for c in range(6)], dtype=_NP_DTYPE + ) + corporate_taxation_choices = np.array( + [0.0 + 0.2 * c for c in range(6)], dtype=_NP_DTYPE + ) + tax_choices = np.array( + list(itertools.product(income_taxation_choices, corporate_taxation_choices)), + dtype=_NP_DTYPE, + ) + global_state_dim = ( + NUMFIRMS # prices + + NUMFIRMS # wages + + NUMFIRMS # stocks + + NUMFIRMS # was good overdemanded + + 2 * NUMGOVERNMENTS # tax rates + + 1 + ) # time + + global_state_digit_dims = list( + range(2 * NUMFIRMS, 3 * NUMFIRMS) + ) # stocks are the only global state var that can get huge + consumer_state_dim = ( + global_state_dim + 1 + 1 + ) # budget # theta, the disutility of work + + firm_state_dim = ( + global_state_dim + + 1 # budget + + 1 # capital + + 1 # production alpha + + NUMFIRMS # onehot specifying which firm + ) + + episodes_to_anneal_firm = 100000 + episodes_to_anneal_government = 100000 + government_phase1_start = 100000 + government_state_dim = global_state_dim + DEFAULT_CFG_DICT = { + # actions_array key will be added below + "agents": { + "num_consumers": NUMCONSUMERS, + "num_firms": NUMFIRMS, + "num_governments": NUMGOVERNMENTS, + "global_state_dim": global_state_dim, + "consumer_state_dim": consumer_state_dim, + # action vectors are how much consume from each firm, + # how much to work, and which firm to choose + "consumer_action_dim": NUMFIRMS + 1 + 1, + "consumer_num_consume_actions": consumption_choices.shape[0], + "consumer_num_work_actions": work_choices.shape[0], + "consumer_num_whichfirm_actions": NUMFIRMS, + "firm_state_dim": firm_state_dim, # what are observations? + # actions are price and wage for own firm, and capital choices + "firm_action_dim": 3, + "firm_num_actions": price_and_wage.shape[0], + "government_state_dim": government_state_dim, + "government_action_dim": 2, + "government_num_actions": tax_choices.shape[0], + "max_possible_consumption": float(consumption_choices.max()), + "max_possible_hours_worked": float(work_choices.max()), + "max_possible_wage": float(wage_choices.max()), + "max_possible_price": float(price_choices.max()), + # these are dims which, due to being on a large scale, + # have to be expanded to a digit representation + "consumer_digit_dims": global_state_digit_dims + + [global_state_dim], # global state + consumer budget + # global state + firm budget (do we need capital?) + "firm_digit_dims": global_state_digit_dims + [global_state_dim], + # govt only has global state + "government_digit_dims": global_state_digit_dims, + "firm_reward_scale": 10000, + "government_reward_scale": 100000, + "consumer_reward_scale": 50.0, + "firm_anneal_wages": { + "anneal_on": True, + "start": 22.0, + "increase_const": float(wage_choices.max() - 22.0) + / (episodes_to_anneal_firm), + "decrease_const": (22.0) / episodes_to_anneal_firm, + }, + "firm_anneal_prices": { + "anneal_on": True, + "start": 1000.0, + "increase_const": float(price_choices.max() - 1000.00) + / episodes_to_anneal_firm, + "decrease_const": (1000.0) / episodes_to_anneal_firm, + }, + "government_anneal_taxes": { + "anneal_on": True, + "start": 0.0, + "increase_const": 1.0 / episodes_to_anneal_government, + }, + "firm_begin_anneal_action": 0, + "government_begin_anneal_action": government_phase1_start, + "consumer_anneal_theta": { + "anneal_on": True, + "exp_decay_length_in_steps": episodes_const, + }, + "consumer_anneal_entropy": { + "anneal_on": True, + "exp_decay_length_in_steps": episodes_const, + "coef_floor": 0.1, + }, + "firm_anneal_entropy": { + "anneal_on": True, + "exp_decay_length_in_steps": episodes_const, + "coef_floor": 0.1, + }, + "govt_anneal_entropy": { + "anneal_on": True, + "exp_decay_length_in_steps": episodes_const, + "coef_floor": 0.1, + }, + "consumer_noponzi_eta": 0.0, + "consumer_penalty_scale": 1.0, + "firm_noponzi_eta": 0.0, + "firm_training_start": episodes_to_anneal_firm, + "government_training_start": government_phase1_start + + episodes_to_anneal_government, + "consumer_training_start": 0, + "government_counts_firm_reward": 0, + "should_boost_firm_reward": False, + "firm_reward_for_government_factor": 0.0025, + }, + "world": { + "maxtime": 10, + "initial_firm_endowment": 22.0 * 1000 * NUMCONSUMERS, + "initial_consumer_endowment": 2000, + "initial_stocks": 0.0, + "initial_prices": 1000.0, + "initial_wages": 22.0, + "interest_rate": 0.1, + "consumer_theta": 0.01, + "crra_param": 0.1, + "production_alpha": "fixed_array", # only works for exactly 10 firms, kluge + "initial_capital": "twolevel", + "paretoscaletheta": 4.0, + "importer_price": 500.0, + "importer_quantity": 100.0, + "use_importer": 1, + }, + "train": { + "batch_size": 8, + "base_seed": 1234, + "save_dense_every": 2000, + "save_model_every": 10000, + "num_episodes": 500000, + "infinite_episodes": False, + "lr": 0.01, + "gamma": 0.9999, + "entropy": 0.0, + "value_loss_weight": 1.0, + "digit_representation_size": 10, + "lagr_num_steps": 1, + "boost_firm_reward_factor": 1.0, + }, + } + return ( + DEFAULT_CFG_DICT, + consumption_choices, + work_choices, + price_and_wage, + tax_choices, + None, + None, + ) + + +def all_agents_short_export_experiment_template( + NUMFIRMS, NUMCONSUMERS, NUMGOVERNMENTS, episodes_const=10000 +): + consumption_choices = [ + np.array([0.0 + 1.0 * c for c in range(11)], dtype=_NP_DTYPE) + ] + work_choices = [ + np.array([0.0 + 20 * 13 * h for h in range(5)], dtype=_NP_DTYPE) + ] # specify dtype -- be consistent? + + consumption_choices = np.array( + list(itertools.product(*consumption_choices)), dtype=_NP_DTYPE + ) + work_choices = np.array(list(itertools.product(*work_choices)), dtype=_NP_DTYPE) + + price_choices = np.array([0.0 + 500.0 * c for c in range(6)], dtype=_NP_DTYPE) + wage_choices = np.array([0.0, 11.0, 22.0, 33.0, 44.0], dtype=_NP_DTYPE) + capital_choices = np.array([0.1], dtype=_NP_DTYPE) + price_and_wage = np.array( + list(itertools.product(price_choices, wage_choices, capital_choices)), + dtype=_NP_DTYPE, + ) + + # government action discretization + income_taxation_choices = np.array( + [0.0 + 0.2 * c for c in range(6)], dtype=_NP_DTYPE + ) + corporate_taxation_choices = np.array( + [0.0 + 0.2 * c for c in range(6)], dtype=_NP_DTYPE + ) + tax_choices = np.array( + list(itertools.product(income_taxation_choices, corporate_taxation_choices)), + dtype=_NP_DTYPE, + ) + global_state_dim = ( + NUMFIRMS # prices + + NUMFIRMS # wages + + NUMFIRMS # stocks + + NUMFIRMS # was good overdemanded + + 2 * NUMGOVERNMENTS # tax rates + + 1 + ) # time + + global_state_digit_dims = list( + range(2 * NUMFIRMS, 3 * NUMFIRMS) + ) # stocks are the only global state var that can get huge + consumer_state_dim = ( + global_state_dim + 1 + 1 + ) # budget # theta, the disutility of work + + firm_state_dim = ( + global_state_dim + + 1 # budget + + 1 # capital + + 1 # production alpha + + NUMFIRMS # onehot specifying which firm + ) + + episodes_to_anneal_firm = 30000 + episodes_to_anneal_government = 30000 + government_phase1_start = 30000 + government_state_dim = global_state_dim + DEFAULT_CFG_DICT = { + # actions_array key will be added below + "agents": { + "num_consumers": NUMCONSUMERS, + "num_firms": NUMFIRMS, + "num_governments": NUMGOVERNMENTS, + "global_state_dim": global_state_dim, + "consumer_state_dim": consumer_state_dim, + # action vectors are how much consume from each firm, + # how much to work, and which firm to choose + "consumer_action_dim": NUMFIRMS + 1 + 1, + "consumer_num_consume_actions": consumption_choices.shape[0], + "consumer_num_work_actions": work_choices.shape[0], + "consumer_num_whichfirm_actions": NUMFIRMS, + "firm_state_dim": firm_state_dim, # what are observations? + # actions are price and wage for own firm, and capital choices + "firm_action_dim": 3, + "firm_num_actions": price_and_wage.shape[0], + "government_state_dim": government_state_dim, + "government_action_dim": 2, + "government_num_actions": tax_choices.shape[0], + "max_possible_consumption": float(consumption_choices.max()), + "max_possible_hours_worked": float(work_choices.max()), + "max_possible_wage": float(wage_choices.max()), + "max_possible_price": float(price_choices.max()), + # these are dims which, due to being on a large scale, + # have to be expanded to a digit representation + "consumer_digit_dims": global_state_digit_dims + + [global_state_dim], # global state + consumer budget + "firm_digit_dims": global_state_digit_dims + + [global_state_dim], # global state + firm budget (do we need capital?) + # govt only has global state + "government_digit_dims": global_state_digit_dims, + "firm_reward_scale": 10000, + "government_reward_scale": 100000, + "consumer_reward_scale": 50.0, + "firm_anneal_wages": { + "anneal_on": True, + "start": 22.0, + "increase_const": float(wage_choices.max() - 22.0) + / (episodes_to_anneal_firm), + "decrease_const": (22.0) / episodes_to_anneal_firm, + }, + "firm_anneal_prices": { + "anneal_on": True, + "start": 1000.0, + "increase_const": float(price_choices.max() - 1000.00) + / episodes_to_anneal_firm, + "decrease_const": (1000.0) / episodes_to_anneal_firm, + }, + "government_anneal_taxes": { + "anneal_on": True, + "start": 0.0, + "increase_const": 1.0 / episodes_to_anneal_government, + }, + "firm_begin_anneal_action": 0, + "government_begin_anneal_action": government_phase1_start, + "consumer_anneal_theta": { + "anneal_on": True, + "exp_decay_length_in_steps": episodes_const, + }, + "consumer_anneal_entropy": { + "anneal_on": True, + "exp_decay_length_in_steps": episodes_const, + "coef_floor": 0.1, + }, + "firm_anneal_entropy": { + "anneal_on": True, + "exp_decay_length_in_steps": episodes_const, + "coef_floor": 0.1, + }, + "govt_anneal_entropy": { + "anneal_on": True, + "exp_decay_length_in_steps": episodes_const, + "coef_floor": 0.1, + }, + "consumer_noponzi_eta": 0.0, + "consumer_penalty_scale": 1.0, + "firm_noponzi_eta": 0.0, + "firm_training_start": episodes_to_anneal_firm, + "government_training_start": government_phase1_start + + episodes_to_anneal_government, + "consumer_training_start": 0, + "government_counts_firm_reward": 0, + "should_boost_firm_reward": False, + "firm_reward_for_government_factor": 0.0025, + }, + "world": { + "maxtime": 10, + "initial_firm_endowment": 22.0 * 1000 * NUMCONSUMERS, + "initial_consumer_endowment": 2000, + "initial_stocks": 0.0, + "initial_prices": 1000.0, + "initial_wages": 22.0, + "interest_rate": 0.1, + "consumer_theta": 0.01, + "crra_param": 0.1, + "production_alpha": "fixed_array", # only works for exactly 10 firms, kluge + "initial_capital": "twolevel", + "paretoscaletheta": 4.0, + "importer_price": 500.0, + "importer_quantity": 100.0, + "use_importer": 1, + }, + "train": { + "batch_size": 8, + "base_seed": 1234, + "save_dense_every": 2000, + "save_model_every": 10000, + "num_episodes": 200000, + "infinite_episodes": False, + "lr": 0.01, + "gamma": 0.9999, + "entropy": 0.0, + "value_loss_weight": 1.0, + "digit_representation_size": 10, + "lagr_num_steps": 1, + "boost_firm_reward_factor": 1.0, + }, + } + return ( + DEFAULT_CFG_DICT, + consumption_choices, + work_choices, + price_and_wage, + tax_choices, + None, + None, + ) + + +def very_short_test_template( + NUMFIRMS, NUMCONSUMERS, NUMGOVERNMENTS, episodes_const=30000 +): + consumption_choices = [ + np.array([0.0 + 1.0 * c for c in range(11)], dtype=_NP_DTYPE) + ] + work_choices = [ + np.array([0.0 + 20 * 13 * h for h in range(5)], dtype=_NP_DTYPE) + ] # specify dtype -- be consistent? + + consumption_choices = np.array( + list(itertools.product(*consumption_choices)), dtype=_NP_DTYPE + ) + work_choices = np.array(list(itertools.product(*work_choices)), dtype=_NP_DTYPE) + + price_choices = np.array([0.0 + 500.0 * c for c in range(6)], dtype=_NP_DTYPE) + wage_choices = np.array([0.0, 11.0, 22.0, 33.0, 44.0], dtype=_NP_DTYPE) + capital_choices = np.array([0.1], dtype=_NP_DTYPE) + price_and_wage = np.array( + list(itertools.product(price_choices, wage_choices, capital_choices)), + dtype=_NP_DTYPE, + ) + + # government action discretization + income_taxation_choices = np.array( + [0.0 + 0.2 * c for c in range(6)], dtype=_NP_DTYPE + ) + corporate_taxation_choices = np.array( + [0.0 + 0.2 * c for c in range(6)], dtype=_NP_DTYPE + ) + tax_choices = np.array( + list(itertools.product(income_taxation_choices, corporate_taxation_choices)), + dtype=_NP_DTYPE, + ) + global_state_dim = ( + NUMFIRMS # prices + + NUMFIRMS # wages + + NUMFIRMS # stocks + + NUMFIRMS # was good overdemanded + + 2 * NUMGOVERNMENTS # tax rates + + 1 + ) # time + + global_state_digit_dims = list( + range(2 * NUMFIRMS, 3 * NUMFIRMS) + ) # stocks are the only global state var that can get huge + consumer_state_dim = ( + global_state_dim + 1 + 1 + ) # budget # theta, the disutility of work + + firm_state_dim = ( + global_state_dim + + 1 # budget + + 1 # capital + + 1 # production alpha + + NUMFIRMS # onehot specifying which firm + ) + + episodes_to_anneal_firm = 10 + episodes_to_anneal_government = 10 + government_phase1_start = 10 + government_state_dim = global_state_dim + DEFAULT_CFG_DICT = { + # actions_array key will be added below + "agents": { + "num_consumers": NUMCONSUMERS, + "num_firms": NUMFIRMS, + "num_governments": NUMGOVERNMENTS, + "global_state_dim": global_state_dim, + "consumer_state_dim": consumer_state_dim, + # action vectors are how much consume from each firm, + # how much to work, and which firm to choose + "consumer_action_dim": NUMFIRMS + 1 + 1, + "consumer_num_consume_actions": consumption_choices.shape[0], + "consumer_num_work_actions": work_choices.shape[0], + "consumer_num_whichfirm_actions": NUMFIRMS, + "firm_state_dim": firm_state_dim, # what are observations? + # actions are price and wage for own firm, and capital choices + "firm_action_dim": 3, + "firm_num_actions": price_and_wage.shape[0], + "government_state_dim": government_state_dim, + "government_action_dim": 2, + "government_num_actions": tax_choices.shape[0], + "max_possible_consumption": float(consumption_choices.max()), + "max_possible_hours_worked": float(work_choices.max()), + "max_possible_wage": float(wage_choices.max()), + "max_possible_price": float(price_choices.max()), + # these are dims which, due to being on a large scale, + # have to be expanded to a digit representation + "consumer_digit_dims": global_state_digit_dims + + [global_state_dim], # global state + consumer budget + "firm_digit_dims": global_state_digit_dims + + [global_state_dim], # global state + firm budget (do we need capital?) + # govt only has global state + "government_digit_dims": global_state_digit_dims, + "firm_reward_scale": 10000, + "government_reward_scale": 100000, + "consumer_reward_scale": 50.0, + "firm_anneal_wages": { + "anneal_on": True, + "start": 22.0, + "increase_const": float(wage_choices.max() - 22.0) + / (episodes_to_anneal_firm), + "decrease_const": (22.0) / episodes_to_anneal_firm, + }, + "firm_anneal_prices": { + "anneal_on": True, + "start": 1000.0, + "increase_const": float(price_choices.max() - 1000.00) + / episodes_to_anneal_firm, + "decrease_const": (1000.0) / episodes_to_anneal_firm, + }, + "government_anneal_taxes": { + "anneal_on": True, + "start": 0.0, + "increase_const": 1.0 / episodes_to_anneal_government, + }, + "firm_begin_anneal_action": 0, + "government_begin_anneal_action": government_phase1_start, + "consumer_anneal_theta": { + "anneal_on": True, + "exp_decay_length_in_steps": episodes_const, + }, + "consumer_anneal_entropy": { + "anneal_on": True, + "exp_decay_length_in_steps": episodes_const, + "coef_floor": 0.1, + }, + "firm_anneal_entropy": { + "anneal_on": True, + "exp_decay_length_in_steps": episodes_const, + "coef_floor": 0.1, + }, + "govt_anneal_entropy": { + "anneal_on": True, + "exp_decay_length_in_steps": episodes_const, + "coef_floor": 0.1, + }, + "consumer_noponzi_eta": 0.0, + "consumer_penalty_scale": 1.0, + "firm_noponzi_eta": 0.0, + "firm_training_start": episodes_to_anneal_firm, + "government_training_start": government_phase1_start + + episodes_to_anneal_government, + "consumer_training_start": 0, + "government_counts_firm_reward": 0, + "should_boost_firm_reward": False, + "firm_reward_for_government_factor": 0.0025, + "train_firms_every": 2, + "train_consumers_every": 1, + "train_government_every": 5, + }, + "world": { + "maxtime": 10, + "initial_firm_endowment": 22.0 * 1000 * NUMCONSUMERS, + "initial_consumer_endowment": 2000, + "initial_stocks": 0.0, + "initial_prices": 1000.0, + "initial_wages": 22.0, + "interest_rate": 0.1, + "consumer_theta": 0.01, + "crra_param": 0.1, + "production_alpha": "fixed_array", # only works for exactly 10 firms, kluge + "initial_capital": "twolevel", + "paretoscaletheta": 4.0, + "importer_price": 500.0, + "importer_quantity": 100.0, + "use_importer": 1, + }, + "train": { + "batch_size": 8, + "base_seed": 1234, + "save_dense_every": 2000, + "save_model_every": 10000, + "num_episodes": 100, + "infinite_episodes": False, + "lr": 0.01, + "gamma": 0.9999, + "entropy": 0.0, + "value_loss_weight": 1.0, + "digit_representation_size": 10, + "lagr_num_steps": 1, + "boost_firm_reward_factor": 1.0, + }, + } + return ( + DEFAULT_CFG_DICT, + consumption_choices, + work_choices, + price_and_wage, + tax_choices, + None, + None, + ) + + +def global_state_scaling_factors(cfg_dict): + max_wage = cfg_dict["agents"]["max_possible_wage"] + max_price = cfg_dict["agents"]["max_possible_price"] + num_firms = cfg_dict["agents"]["num_firms"] + num_governments = cfg_dict["agents"]["num_governments"] + maxtime = cfg_dict["world"]["maxtime"] + + digit_size = cfg_dict["train"]["digit_representation_size"] + + return torch.tensor( + # prices, wages, stocks, overdemanded + ([max_price] * num_firms) + + ([max_wage] * num_firms) + + ([1.0] * num_firms * digit_size) # stocks are expanded to digit form + + ([1.0] * num_firms) + + ([1.0] * (2 * num_governments)) + + [maxtime] + ) + + +def consumer_state_scaling_factors(cfg_dict): + global_state_scales = global_state_scaling_factors(cfg_dict) + digit_size = cfg_dict["train"]["digit_representation_size"] + consumer_scales = torch.tensor( + ([1.0] * digit_size) + [cfg_dict["world"]["consumer_theta"]] + ) + return torch.cat((global_state_scales, consumer_scales)).cuda() + + +def firm_state_scaling_factors(cfg_dict): + num_firms = cfg_dict["agents"]["num_firms"] + global_state_scales = global_state_scaling_factors(cfg_dict) + digit_size = cfg_dict["train"]["digit_representation_size"] + # budget, capital, alpha, one-hot + firm_scales = torch.tensor( + ([1.0] * digit_size) + [10000.0, 1.0] + ([1.0] * num_firms) + ) + return torch.cat((global_state_scales, firm_scales)).cuda() + + +def govt_state_scaling_factors(cfg_dict): + return global_state_scaling_factors(cfg_dict).cuda() diff --git a/ai_economist/real_business_cycle/rbc/cuda/firm_rbc.cu b/ai_economist/real_business_cycle/rbc/cuda/firm_rbc.cu new file mode 100644 index 0000000..84fce67 --- /dev/null +++ b/ai_economist/real_business_cycle/rbc/cuda/firm_rbc.cu @@ -0,0 +1,912 @@ +// Copyright (c) 2021, salesforce.com, inc. +// All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// For full license text, see the LICENSE file in the repo root +// or https://opensource.org/licenses/BSD-3-Clause + + +// Real Business Chain implementation in CUDA C + +#include +#include + +typedef enum { + kConsumerType, + kFirmType, + kGovernmentType, +} AgentType; + +const size_t kBatchSize = M_BATCHSIZE; +const size_t kNumConsumers = M_NUMCONSUMERS; +const bool kCountFirmReward = M_COUNTFIRMREWARD; +const size_t kNumFirms = M_NUMFIRMS; +const size_t kNumGovts = M_NUMGOVERNMENTS; +const float kMaxTime = M_MAXTIME; +const float kCrraParam = M_CRRA_PARAM; +const float kInterestRate = M_INTERESTRATE; +const size_t kNumAgents = kNumConsumers + kNumFirms + kNumGovts; +const bool kIncentivizeFirmActivity = M_SHOULDBOOSTFIRMREWARD; +const float kFirmBoostRewardFactor = M_BOOSTFIRMREWARDFACTOR; +const bool kUseImporter = M_USEIMPORTER; +const float kImporterPrice = M_IMPORTERPRICE; +const float kImporterQuantity = M_IMPORTERQUANTITY; +const float kLaborFloor = M_LABORFLOOR; + +// Global state = +const size_t kNumPrices = kNumFirms; // - prices, +const size_t kNumWages = kNumFirms; // - wages, +const size_t kNumInventories = kNumFirms; // - stocks, +const size_t kNumOverdemandFlags = kNumFirms; // - good overdemanded flag, +const size_t kNumCorporateTaxes = kNumGovts; // - corporate tax rate +const size_t kNumIncomeTaxes = kNumGovts; // - income tax rate +const size_t kNumTimeDimensions = 1; // - time step +const size_t kGlobalStateSize = kNumPrices + kNumWages + kNumInventories + kNumOverdemandFlags + kNumCorporateTaxes + kNumIncomeTaxes + kNumTimeDimensions; + +const size_t kIdxPricesOffset = 0; +const size_t kIdxWagesOffset = kNumPrices; +const size_t kIdxStockOffset = kIdxWagesOffset + kNumInventories; +const size_t kIdxOverdemandOffset = kIdxStockOffset + kNumOverdemandFlags; +const size_t kIdxIncomeTaxOffset = kGlobalStateSize - 3; +const size_t kIdxCorporateTaxOffset = kGlobalStateSize - 2; +const size_t kIdxTimeOffset = kGlobalStateSize - 1; + +// Consumer actions: consume, work, choose which firm to work for +const size_t kActionSizeConsumer = kNumFirms + 1 + 1; + +// add budget and theta +const size_t kStateSizeConsumer = kGlobalStateSize + 1 + 1; +const size_t kIdxConsumerBudgetOffset = 0; + +// offset from agent-specific state part of array +const size_t kIdxConsumerThetaOffset = 1; + +// UNUSED for consumer. Actions are floats. +// __constant__ float cs_index_to_action[num_actions_consumer * +// kActionSizeConsumer]; const size_t kActionSizeConsumer = kNumFirms + +// kNumFirms; // consume + work +/*const size_t num_actions_consumer = + NUMACTIONSkConsumerType; // depends on discretization*/ + +// Firm actions: set wage, set price, invest in capital +const size_t kActionSizeFirm = 3; + +// Number of actions depends on discretization of continuous action space. +const size_t kNumActionsFirm = M_NUMACTIONSFIRM; + +// budget, capital, production alpha, and one-hot firm ID +const size_t kStateSizeFirm = kGlobalStateSize + 1 + 1 + 1 + kNumFirms; + +// offset from agent-specific state part of array +const size_t kIdxFirmBudgetOffset = 0; +const size_t kIdxFirmCapitalOffset = 1; +const size_t kIdxFirmAlphaOffset = 2; +const size_t kIdxFirmOnehotOffset = 3; + +// Constant memory available from ALL threads. +// See https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#constant +__constant__ float kFirmIndexToAction[kNumActionsFirm * kActionSizeFirm]; + +// Corporate + income tax rates +const size_t kGovtActionSize = 2; +const size_t kNumActionsGovernment = M_NUMACTIONSGOVERNMENT; +const size_t kGovtStateSize = kGlobalStateSize; +__constant__ float + kGovernmentIndexToAction[kNumActionsGovernment * kGovtActionSize]; + +// One RNG state for each thread. Each thread is assigned to an agent in an env. +__device__ curandState_t + *rng_state_arr[kBatchSize * kNumAgents]; // not sure best way to do this + +// Offsets into action vectors +const size_t kIdxConsumerDemandedOffset = 0; +const size_t kIdxConsumerWorkedOffset = kNumFirms; +const size_t kIdxConsumerWhichFirmOffset = kNumFirms + 1; + +// currently 1 govt +const size_t kIdxThisThreadGovtId = 0; + +extern "C" { + +// ------------------ +// CUDA C Utilities +// ------------------ +__device__ void CopyFloatArraySlice(float *start_point, int num_elems, + float *destination) { + for (int i = 0; i < num_elems; i++) { + destination[i] = start_point[i]; + } +} + +__device__ void CopyIntArraySlice(int *start_point, int num_elems, + float *destination) { + for (int i = 0; i < num_elems; i++) { + destination[i] = start_point[i]; + } +} + +// unfortunately, you can't do templates with extern "C" linkage required for +// CUDA, so we have to define different functions for each case. +__device__ int *GetPointerFromMultiIndexFor3DIntTensor(int *array, + const dim3 &sizes, + const dim3 &index) { + unsigned int flat_index = + index.z + index.y * (sizes.z) + index.x * (sizes.z * sizes.y); + return &(array[flat_index]); +} + +__device__ float *GetPointerFromMultiIndexFor3DFloatTensor(float *array, + const dim3 &sizes, + const dim3 &index) { + unsigned int flat_index = + index.z + index.y * (sizes.z) + index.x * (sizes.z * sizes.y); + return &(array[flat_index]); +} + +__device__ float * +GetPointerFromMultiIndexFor4DTensor(float *array, const size_t *sizes, + const size_t *multi_index) { + // don't use this for arrays that arne't exactly size 4!!! + unsigned int flat_index = multi_index[3] + multi_index[2] * sizes[3] + + multi_index[1] * sizes[3] * sizes[2] + + multi_index[0] * sizes[3] * sizes[2] * sizes[1]; + return &(array[flat_index]); +} + +__global__ void CudaInitKernel(int seed) { + // we want to reset random seeds for all firms and consumers + int tidx = threadIdx.x; + const int kThisThreadGlobalArrayIdx = blockIdx.x * kNumAgents + threadIdx.x; + + if (tidx < kNumAgents) { + curandState_t *s = new curandState_t; + if (s != 0) { + curand_init(seed, kThisThreadGlobalArrayIdx, 0, s); + } + rng_state_arr[kThisThreadGlobalArrayIdx] = s; + } +} + +__global__ void CudaFreeRand() { + int tidx = threadIdx.x; + const int kThisThreadGlobalArrayIdx = blockIdx.x * kNumAgents + threadIdx.x; + + if (tidx < kNumAgents) { + curandState_t *s = rng_state_arr[kThisThreadGlobalArrayIdx]; + delete s; + } +} + +__device__ int SearchIndex(float *distr, float p, int l, int r) { + int mid; + int left = l; + int right = r; + + while (left <= right) { + mid = left + (right - left) / 2; + if (distr[mid] == p) { + return mid; + } else if (distr[mid] < p) { + left = mid + 1; + } else { + right = mid - 1; + } + } + return left > r ? r : left; +} + +// -------------------- +// Simulation Utilities +// -------------------- +__device__ AgentType GetAgentType(const int agent_id) { + if (agent_id < kNumConsumers) { + return kConsumerType; + } else if (agent_id < (kNumConsumers + kNumFirms)) { + return kFirmType; + } else { + return kGovernmentType; + } +} + +__device__ float GetCRRAUtil(float consumption, float crra_param) { + return (powf(consumption + 1, 1.0 - crra_param) - 1.0) / (1.0 - crra_param); +} + +__global__ void CudaResetEnv(float *cs_state_arr, float *fm_state_arr, + float *govt_state_arr, float *cs_state_ckpt_arr, + float *fm_state_ckpt_arr, + float *govt_state_ckpt_arr, + float theta_anneal_factor) { + /* + Resets the environment by writing the initial state (checkpoint) into the + state array for this agent (thread). + */ + + const int kBlockId = blockIdx.x; + const int kWithinBlockAgentId = threadIdx.x; + + if (kWithinBlockAgentId >= kNumAgents) { + return; + } + + AgentType ThisThreadAgentType = GetAgentType(kWithinBlockAgentId); + + float *state_arr; + float *ckpt_arr; + dim3 my_state_shape, my_state_idx; + size_t my_state_size; + + if (ThisThreadAgentType == kConsumerType) { + // This thread/agent is a consumer. + my_state_size = kStateSizeConsumer; + my_state_shape = {kBatchSize, kNumConsumers, kStateSizeConsumer}; + my_state_idx = {kBlockId, kWithinBlockAgentId, 0}; + state_arr = cs_state_arr; + ckpt_arr = cs_state_ckpt_arr; + } else if (ThisThreadAgentType == kFirmType) { + // This thread/agent is a firm. + my_state_size = kStateSizeFirm; + my_state_shape = {kBatchSize, kNumFirms, kStateSizeFirm}; + my_state_idx = {kBlockId, + (unsigned int)(kWithinBlockAgentId - kNumConsumers), 0}; + state_arr = fm_state_arr; + ckpt_arr = fm_state_ckpt_arr; + } else { + // This thread/agent is government. + my_state_size = kGovtStateSize; + my_state_shape = {kBatchSize, kNumGovts, kGovtStateSize}; + my_state_idx = { + kBlockId, + (unsigned int)(kWithinBlockAgentId - kNumConsumers - kNumFirms), 0}; + state_arr = govt_state_arr; + ckpt_arr = govt_state_ckpt_arr; + } + + float *my_state_arr = GetPointerFromMultiIndexFor3DFloatTensor( + state_arr, my_state_shape, my_state_idx); + + float *my_ckpt_ptr = GetPointerFromMultiIndexFor3DFloatTensor( + ckpt_arr, my_state_shape, my_state_idx); + + CopyFloatArraySlice(my_ckpt_ptr, my_state_size, my_state_arr); + + // anneal theta + if (ThisThreadAgentType == kConsumerType) { + my_state_arr[kGlobalStateSize + kIdxConsumerThetaOffset] *= + theta_anneal_factor; + } +} + +__device__ void GetAction(float *action_arr, float *index_to_action_arr, + int index, int agent_idx, int agent_action_size) { + + // it needs to be possible to call this for either agents or firms + // Note: each thread is an agent. + for (int i = 0; i < agent_action_size; i++) { + action_arr[agent_idx * agent_action_size + i] = + index_to_action_arr[index * agent_action_size + i]; + } +} + +__global__ void CudaSampleFirmAndGovernmentActions( + float *fm_distr, int *fm_action_indices_arr, float *fm_actions_arr, + float *govt_distr, int *govt_action_indices_arr, float *govt_actions_arr) { + // Samples actions for firms and governments. Consumer actions are sampled in + // Pytorch... + const int kWithinBlockAgentId = threadIdx.x; + + // Unused threads should not do anything. + if (threadIdx.x >= kNumAgents) { + return; + } + + AgentType ThisThreadAgentType = GetAgentType(kWithinBlockAgentId); + + // Index into rand states array + int kThisThreadGlobalArrayIdx = blockIdx.x * kNumAgents + threadIdx.x; + curandState_t rng_state = *rng_state_arr[kThisThreadGlobalArrayIdx]; + *rng_state_arr[kThisThreadGlobalArrayIdx] = rng_state; + + // float cs_cum_dist[num_actions_consumer]; + float fm_cum_dist[kNumActionsFirm]; + float govt_cum_dist[kNumActionsGovernment]; + + float *my_cumul_dist; + float *my_dist; + int *my_indices; + float *my_actions; + float *index_to_action; + int this_thread_global_array_idx; + size_t my_num_actions; + int my_action_size; + + // Consumers have multiple action heads, hence sampling is more complicated. + if (ThisThreadAgentType == kConsumerType) { + return; + } else if (ThisThreadAgentType == kFirmType) { + // on firm thread + my_cumul_dist = fm_cum_dist; + my_dist = fm_distr; + my_indices = fm_action_indices_arr; + my_actions = fm_actions_arr; + my_num_actions = kNumActionsFirm; + index_to_action = kFirmIndexToAction; + my_action_size = kActionSizeFirm; + this_thread_global_array_idx = + (blockIdx.x * kNumFirms) + (threadIdx.x - kNumConsumers); + } else { + my_cumul_dist = govt_cum_dist; + my_dist = govt_distr; + my_indices = govt_action_indices_arr; + my_actions = govt_actions_arr; + my_num_actions = kNumActionsGovernment; + index_to_action = kGovernmentIndexToAction; + my_action_size = kGovtActionSize; + this_thread_global_array_idx = + (blockIdx.x * kNumGovts) + (threadIdx.x - kNumConsumers - kNumFirms); + } + + // Compute CDF + my_cumul_dist[0] = my_dist[this_thread_global_array_idx * my_num_actions]; + for (int i = 1; i < my_num_actions; i++) { + my_cumul_dist[i] = + my_dist[this_thread_global_array_idx * my_num_actions + i] + + my_cumul_dist[i - 1]; + } + + // Given sampled action which is a float in [0, 1], find the corresponding + // discrete action. + float sampled_float = curand_uniform(&rng_state); + const int index = + SearchIndex(my_cumul_dist, sampled_float, 0, (int)(my_num_actions - 1)); + my_indices[this_thread_global_array_idx] = index; + GetAction(my_actions, index_to_action, index, this_thread_global_array_idx, + my_action_size); +} + +__device__ float GetFirmProduction(float technology, float capital, float hours, + float alpha) { + if (hours < kLaborFloor) { + hours = 0.0; + } + return technology * powf(capital, 1.0 - alpha) * powf(hours, alpha); +} + +// -------------------- +// Simulation Logic +// -------------------- +__global__ void +CudaStep(float *cs_state_arr, float *cs_actions_arr, float *cs_rewards_arr, + float *cs_state_arr_batch, float *cs_rewards_arr_batch, + + float *fm_state_arr, int *fm_action_indices_arr, float *fm_actions_arr, + float *fm_rewards_arr, float *fm_state_arr_batch, + int *fm_actions_arr_batch, float *fm_rewards_arr_batch, + + float *govt_state_arr, int *govt_action_indices_arr, + float *govt_actions_arr, float *govt_rewards_arr, + float *govt_state_arr_batch, int *govt_actions_arr_batch, + float *govt_rewards_arr_batch, + float *consumer_aux_batch, + float *firm_aux_batch, + int iter) { + // This function should be called with 1 block per copy of the environment. + // Within a block, each agent should have a thread. + const int kWithinBlockAgentId = threadIdx.x; + + // return if we're on an extra thread not corresponding to an agent + if (kWithinBlockAgentId >= kNumAgents) { + return; + } + + // ------------------------------------- + // Start of variables and pointers defs. + // ------------------------------------- + + // __shared__ variables are block-local: can be seen by each thread ** in the + // block ** + __shared__ float gross_demand_arr[kNumFirms]; + __shared__ int num_consumer_demand_arr[kNumFirms]; + __shared__ float hours_worked_arr[kNumFirms]; + __shared__ float total_actually_consumer_arr[kNumFirms]; + __shared__ float bought_by_importer_arr[kNumFirms]; + __shared__ float next_global_state_arr[kGlobalStateSize]; + __shared__ float tax_revenue_arr[kNumGovts]; + __shared__ float total_utility_arr[kNumGovts]; + __shared__ bool need_to_ration_this_good_arr[kNumFirms]; // whether or not to + // ration good i + + float net_demand_arr[kNumFirms]; // amount demanded after budget constraints + // by a consumer (ignore for non-consumers) + + int num_iter = (int)kMaxTime; + AgentType ThisThreadAgentType = GetAgentType(kWithinBlockAgentId); + float this_agent_reward = 0.0; + + // pointer to start of state vector + // state vector consists of global state, then + // agent-specific state global part is of same size for + // all agents, but needs to be sliced out of different + // arrays depending on agent type + float *my_global_state_ptr; + + float *my_action_arr; + + // pointer to start of state vector in batch history + float *batch_state_ptr; + + // sizes and indices for strided array access + dim3 my_state_shape, my_state_idx, action_shape; + + // shape for batched array of scalars (action ind and reward) + dim3 batch_single_shape, single_idx; + + // pointer to action index + int *batch_action_value_ptr; + + // pointer to batch reward + float *batch_reward_value_ptr; + + // pointers to action index for current arrays + int *my_action_value_ptr; + + // pointers to reward index for current arrays + float *my_reward_value_ptr; + + float *my_aux_batch_ptr; + + // ----------------------------------- + // End of variables and pointers defs. + // ----------------------------------- + + if (ThisThreadAgentType == kConsumerType) { + // get current state + my_state_shape = {kBatchSize, kNumConsumers, kStateSizeConsumer}; + my_state_idx = {blockIdx.x, threadIdx.x, 0}; + my_global_state_ptr = GetPointerFromMultiIndexFor3DFloatTensor( + cs_state_arr, my_state_shape, my_state_idx); // index) + + // get current action + action_shape = {kBatchSize, kNumConsumers, kActionSizeConsumer}; + my_action_arr = GetPointerFromMultiIndexFor3DFloatTensor( + cs_actions_arr, action_shape, my_state_idx); + + // index into the episode history and save prev state into it + size_t my_batch_state_shape[] = {kBatchSize, num_iter, kNumConsumers, + kStateSizeConsumer}; + size_t my_batch_state_idx[] = {blockIdx.x, iter, threadIdx.x, 0}; + batch_state_ptr = GetPointerFromMultiIndexFor4DTensor( + cs_state_arr_batch, my_batch_state_shape, my_batch_state_idx); + CopyFloatArraySlice(my_global_state_ptr, kStateSizeConsumer, + batch_state_ptr); + + size_t my_aux_batch_shape[] = {kBatchSize, num_iter, kNumConsumers, kNumFirms}; + my_aux_batch_ptr = GetPointerFromMultiIndexFor4DTensor( + consumer_aux_batch, my_aux_batch_shape, my_batch_state_idx + ); + + + // Extract pointers to rewards, batch and current + batch_single_shape = {kBatchSize, (unsigned int)num_iter, kNumConsumers}; + single_idx = {blockIdx.x, (unsigned int)iter, threadIdx.x}; + + batch_reward_value_ptr = GetPointerFromMultiIndexFor3DFloatTensor( + cs_rewards_arr_batch, batch_single_shape, single_idx); + + my_reward_value_ptr = + &(cs_rewards_arr[blockIdx.x * kNumConsumers + threadIdx.x]); + } + + if (ThisThreadAgentType == kFirmType) { + // get current state + size_t this_thread_firm_id = (threadIdx.x - kNumConsumers); + my_state_shape = {kBatchSize, kNumFirms, kStateSizeFirm}; + my_state_idx = {blockIdx.x, (unsigned int)this_thread_firm_id, 0}; + my_global_state_ptr = GetPointerFromMultiIndexFor3DFloatTensor( + fm_state_arr, my_state_shape, my_state_idx); + + // get current action + action_shape = {kBatchSize, kNumFirms, kActionSizeFirm}; + my_action_arr = GetPointerFromMultiIndexFor3DFloatTensor( + fm_actions_arr, action_shape, my_state_idx); + + // index into the episode history and save prev state into it + size_t my_batch_state_shape[] = {kBatchSize, num_iter, kNumFirms, + kStateSizeFirm}; + size_t my_batch_state_idx[] = {blockIdx.x, iter, this_thread_firm_id, 0}; + batch_state_ptr = GetPointerFromMultiIndexFor4DTensor( + fm_state_arr_batch, my_batch_state_shape, my_batch_state_idx); + CopyFloatArraySlice(my_global_state_ptr, kStateSizeFirm, batch_state_ptr); + + dim3 my_aux_batch_shape = {kBatchSize, num_iter, kNumFirms}; + dim3 aux_batch_idx = {blockIdx.x, iter, this_thread_firm_id}; + my_aux_batch_ptr = GetPointerFromMultiIndexFor3DFloatTensor( + firm_aux_batch, my_aux_batch_shape, aux_batch_idx + ); + // extract pointers to action indices and rewards, batch and current + batch_single_shape = {kBatchSize, (unsigned int)num_iter, kNumFirms}; + single_idx = {blockIdx.x, (unsigned int)iter, + (unsigned int)this_thread_firm_id}; + batch_action_value_ptr = GetPointerFromMultiIndexFor3DIntTensor( + fm_actions_arr_batch, batch_single_shape, single_idx); + batch_reward_value_ptr = GetPointerFromMultiIndexFor3DFloatTensor( + fm_rewards_arr_batch, batch_single_shape, single_idx); + + const int kThisThreadFirmIdx = blockIdx.x * kNumFirms + this_thread_firm_id; + my_action_value_ptr = &(fm_action_indices_arr[kThisThreadFirmIdx]); + my_reward_value_ptr = &(fm_rewards_arr[kThisThreadFirmIdx]); + } + + if (ThisThreadAgentType == kGovernmentType) { + + int this_thread_govt_id = (threadIdx.x - kNumConsumers - kNumFirms); + + my_state_shape = {kBatchSize, kNumGovts, kGovtStateSize}; + my_state_idx = {blockIdx.x, (unsigned int)this_thread_govt_id, 0}; + my_global_state_ptr = GetPointerFromMultiIndexFor3DFloatTensor( + govt_state_arr, my_state_shape, my_state_idx); // index) + + // get current action + action_shape = {kBatchSize, kNumGovts, kGovtActionSize}; + my_action_arr = GetPointerFromMultiIndexFor3DFloatTensor( + govt_actions_arr, action_shape, my_state_idx); + + // index into the episode history and save prev state into it + size_t my_batch_state_shape[] = {kBatchSize, num_iter, kNumGovts, + kGovtStateSize}; + size_t my_batch_state_idx[] = {blockIdx.x, iter, this_thread_govt_id, 0}; + batch_state_ptr = GetPointerFromMultiIndexFor4DTensor( + govt_state_arr_batch, my_batch_state_shape, my_batch_state_idx); + CopyFloatArraySlice(my_global_state_ptr, kGovtStateSize, batch_state_ptr); + + // extract pointers to action indices and rewards, batch and current + batch_single_shape = {kBatchSize, (unsigned int)num_iter, kNumGovts}; + single_idx = {blockIdx.x, (unsigned int)iter, + (unsigned int)this_thread_govt_id}; + batch_action_value_ptr = GetPointerFromMultiIndexFor3DIntTensor( + govt_actions_arr_batch, batch_single_shape, single_idx); + batch_reward_value_ptr = GetPointerFromMultiIndexFor3DFloatTensor( + govt_rewards_arr_batch, batch_single_shape, single_idx); + + const int kThisThreadGovtIdx = blockIdx.x * kNumGovts + this_thread_govt_id; + my_action_value_ptr = &(govt_action_indices_arr[kThisThreadGovtIdx]); + my_reward_value_ptr = &(govt_rewards_arr[kThisThreadGovtIdx]); + } + + // ---------------------------------------------- + // State pointers and variables + // Create pointers to agent-specific state that will be updated by the + // simulation logic. + // ---------------------------------------------- + float *my_state_arr = &(my_global_state_ptr[kGlobalStateSize]); + float *prices_arr = &(my_global_state_ptr[kIdxPricesOffset]); + float *wages_arr = &(my_global_state_ptr[kIdxWagesOffset]); + float *available_stock_arr = &(my_global_state_ptr[kIdxStockOffset]); + float time = my_global_state_ptr[kIdxTimeOffset]; + float income_tax_rate = my_global_state_ptr[kIdxIncomeTaxOffset]; + float corporate_tax_rate = my_global_state_ptr[kIdxCorporateTaxOffset]; + + // ------------------------------- + // Safely initialize shared memory + // ------------------------------- + if (ThisThreadAgentType == kFirmType) { + int this_thread_firm_id = threadIdx.x - kNumConsumers; + gross_demand_arr[this_thread_firm_id] = 0.0; + num_consumer_demand_arr[this_thread_firm_id] = 0; + hours_worked_arr[this_thread_firm_id] = 0.0; + total_actually_consumer_arr[this_thread_firm_id] = 0.0; + need_to_ration_this_good_arr[this_thread_firm_id] = false; + } + + if (ThisThreadAgentType == kGovernmentType) { + tax_revenue_arr[0] = 0.0; + } + + __syncthreads(); + // ------------------------------------- + // End - Safely initialize shared memory + // ------------------------------------- + + // ------------------------------------- + // Process actions + // ------------------------------------- + if (ThisThreadAgentType == kConsumerType) { + // amount demanded is just the first part of the action vector + float *this_agent_gross_demand_arr = + &(my_action_arr[kIdxConsumerDemandedOffset]); + const float this_agent_hours_worked = + my_action_arr[kIdxConsumerWorkedOffset]; + const int worked_for_this_firm_id = + (int)my_action_arr[kIdxConsumerWhichFirmOffset]; + + // here, need to scale demands to meet the budget. put them in a local array + // *budgetDemanded logic should be: compute total expenditure given prices. + // if less than budget, copy existing demands + float __cost_of_demand = 0.0; + for (int i = 0; i < kNumFirms; i++) { + __cost_of_demand += this_agent_gross_demand_arr[i] * prices_arr[i]; + } + + // Scale demand to ensure that total demand at most equals total supply + // we want: my_state_arr being 0 always sends __scale_factor to 0 + float __scale_factor = 1.0; + + if ((__cost_of_demand > 0.0) && (__cost_of_demand > my_state_arr[0])) { + __scale_factor = my_state_arr[0] / __cost_of_demand; + } + + // otherwise scale all demands down to meet budget + // copy them into a demanded array + for (int i = 0; i < kNumFirms; i++) { + net_demand_arr[i] = __scale_factor * this_agent_gross_demand_arr[i]; + } + + // adding up demand across threads **in the block** + // somehow store amount demanded per firm in an array demanded (copy from + // action) also store amount worked per firm in array worked + + // Every thread executes atomicAdd_block in a memory-safe way. + for (int i = 0; i < kNumFirms; i++) { + // sum across threads in block + atomicAdd_block(&(gross_demand_arr[i]), net_demand_arr[i]); + + // increment count of consumers who want good i + if (net_demand_arr[i] > 0) { + atomicAdd_block(&(num_consumer_demand_arr[i]), 1); + } + } + + // increment total hours worked for firm i + atomicAdd_block(&(hours_worked_arr[worked_for_this_firm_id]), + this_agent_hours_worked); + } + + // wait for everyone to finish tallying up their adding + __syncthreads(); + + if (ThisThreadAgentType == kFirmType) { + // check each firm if rationing needed + int this_thread_firm_id = threadIdx.x - kNumConsumers; + need_to_ration_this_good_arr[this_thread_firm_id] = + ((gross_demand_arr[this_thread_firm_id] > 0.0) && (gross_demand_arr[this_thread_firm_id] > + available_stock_arr[this_thread_firm_id])); + } + + // wait for single thread to finish checking demands + __syncthreads(); + + // ---------------------------------------- + // Consumers: Rationing demand + Utility + // ---------------------------------------- + // Logic: + // case 1: no overdemand + // case 2: overdemand, but some want less than 1/N -- fill everyone up to + // max(theirs, 1/N) case 3: overdemand, everyone wants more -- fill everyone + // up to max(theirs, 1/N) + float net_consumed_arr[kNumFirms]; // per consumer thread + // always add negligible positive money to avoid budgets becoming small + // negative numbers otherwise, when computing proportions, one may end up with + // negative stocks. + float cs_budget_delta = 0.01; + float fm_budget_delta = 0.01; + float capital_delta = 0.0; + + if (ThisThreadAgentType == kConsumerType) { + // find out how much consumed + for (int i = 0; i < kNumFirms; i++) { + float __ration_factor = 1.0; + + if (need_to_ration_this_good_arr[i]) { + // overdemanded + __ration_factor = available_stock_arr[i] / gross_demand_arr[i]; + } + + net_consumed_arr[i] = __ration_factor * net_demand_arr[i]; + + atomicAdd_block(&(total_actually_consumer_arr[i]), net_consumed_arr[i]); + } + + // store amount actually consumed for this consumer + CopyFloatArraySlice(net_consumed_arr, kNumFirms, my_aux_batch_ptr); + + // ---------------------------------------- + // Compute consumer utility + // ---------------------------------------- + float hours_worked = my_action_arr[kIdxConsumerWorkedOffset]; + int worked_for_this_firm_id = + (int)my_action_arr[kIdxConsumerWhichFirmOffset]; + + // budget is first elem of consumer state, theta second + float __theta = my_state_arr[1]; + + float __this_consumer_util = 0.0; + float __total_hours_worked = 0.0; + float __gross_income = 0.0; + + // Compute expenses + // Each consumer can consume from each firm, so loop over them. + for (int i = 0; i < kNumFirms; i++) { + __this_consumer_util += GetCRRAUtil(net_consumed_arr[i], kCrraParam); + cs_budget_delta -= prices_arr[i] * net_consumed_arr[i]; + } + + // Compute income + __total_hours_worked += hours_worked; + __gross_income += wages_arr[worked_for_this_firm_id] * hours_worked; + float __income_tax_paid = income_tax_rate * __gross_income; + cs_budget_delta += (__gross_income - __income_tax_paid); + + // Update tax revenue (government) + atomicAdd_block(&(tax_revenue_arr[kIdxThisThreadGovtId]), + __income_tax_paid); + + // Compute reward + this_agent_reward += + __this_consumer_util - (__theta / 2.0) * (__total_hours_worked); + } + + __syncthreads(); + + + // ---------------------------------------- + // Firms Exports: Add external consumption. + // ---------------------------------------- + if (ThisThreadAgentType == kFirmType ) { + const int this_thread_firm_id = threadIdx.x - kNumConsumers; + if (kUseImporter) { + // sell remaining goods, if any, to importer, if price is high enough. + float __this_firm_price = prices_arr[this_thread_firm_id]; + float __stock_after_consumers = available_stock_arr[this_thread_firm_id] - total_actually_consumer_arr[this_thread_firm_id]; + + if (__this_firm_price >= kImporterPrice) { + bought_by_importer_arr[this_thread_firm_id] = fmaxf(fminf(__stock_after_consumers, kImporterQuantity), 0.0); // floor to zero to avoid small negative floats + } + else { + bought_by_importer_arr[this_thread_firm_id] = 0.0; + } + } + else { + bought_by_importer_arr[this_thread_firm_id] = 0.0; + } + } + + // ---------------------------------------- + // Firms: Rationing demand + Utility + // ---------------------------------------- + if (ThisThreadAgentType == kFirmType) { + const int this_thread_firm_id = threadIdx.x - kNumConsumers; + + float __this_firm_revenue = + (total_actually_consumer_arr[this_thread_firm_id] + bought_by_importer_arr[this_thread_firm_id]) * + prices_arr[this_thread_firm_id]; + float __wages_paid = + hours_worked_arr[this_thread_firm_id] * wages_arr[this_thread_firm_id]; + + // Firms can invest in new capital. This increases their production factor + // (see GetFirmProduction). + // here, after consumers consume, if price is >= than importer price, importer consumes up to their maximum of the goods, at the importer price + + float __gross_income = __this_firm_revenue - __wages_paid; + capital_delta = fmaxf(my_action_arr[2] * __gross_income, 0.0); + float __gross_profit = __gross_income - capital_delta; + float __corp_tax_paid = corporate_tax_rate * fmaxf(__gross_profit, 0.0); + fm_budget_delta = (__gross_profit - __corp_tax_paid); + if (kIncentivizeFirmActivity) { + if ((fm_budget_delta + my_state_arr[0]) > 0.0) { // if positive budget + this_agent_reward += (kFirmBoostRewardFactor * __this_firm_revenue); + } + } + this_agent_reward += (__gross_profit - __corp_tax_paid); + + atomicAdd_block(&(tax_revenue_arr[0]), __corp_tax_paid); + + float __production = GetFirmProduction(0.01, my_state_arr[kIdxFirmCapitalOffset], + hours_worked_arr[this_thread_firm_id], my_state_arr[kIdxFirmAlphaOffset]); + + // ------------------- + // Update global state + // ------------------- + // update prices in global state + next_global_state_arr[kIdxPricesOffset + this_thread_firm_id] = + my_action_arr[0]; + // update wages in global state + next_global_state_arr[kIdxWagesOffset + this_thread_firm_id] = + my_action_arr[1]; + // update stocks in global state + next_global_state_arr[kIdxStockOffset + this_thread_firm_id] = + available_stock_arr[this_thread_firm_id] - + total_actually_consumer_arr[this_thread_firm_id] - bought_by_importer_arr[this_thread_firm_id] + __production; + + *my_aux_batch_ptr = bought_by_importer_arr[this_thread_firm_id]; + + // update overdemanded in global state + next_global_state_arr[kIdxOverdemandOffset + this_thread_firm_id] = + need_to_ration_this_good_arr[this_thread_firm_id] ? 1.0 : 0.0; + } + + // ----------------- + // Move time forward + // ----------------- + // Let first firm tick time + if (ThisThreadAgentType == kFirmType) { + const int this_thread_firm_id = threadIdx.x - kNumConsumers; + if (this_thread_firm_id == 0) { + next_global_state_arr[kIdxTimeOffset] = + my_global_state_ptr[kIdxTimeOffset] + 1.0; + } + } + + __syncthreads(); + + // ---------------------------------------- + // Subsidies + // ---------------------------------------- + // need to redistribute tax revenues + if (ThisThreadAgentType == kConsumerType) { + float __redistribution = tax_revenue_arr[0] / kNumConsumers; + cs_budget_delta += __redistribution; + } + + // ---------------------------------------- + // Social welfare + // ---------------------------------------- + // After this point consumers and firms know their final reward, so can inform + // the government thread via shared memory + + __syncthreads(); + + // ---------------------------------------- + // Government sets taxes for the next round + // ---------------------------------------- + if (ThisThreadAgentType == kGovernmentType) { + next_global_state_arr[kIdxIncomeTaxOffset] = my_action_arr[0]; + next_global_state_arr[kIdxCorporateTaxOffset] = my_action_arr[1]; + } + + __syncthreads(); + + // ----------------------------------------------- + // Copy next_global_state_arr into my global state + // ----------------------------------------------- + // All agents need to see the updated global state + CopyFloatArraySlice(next_global_state_arr, kGlobalStateSize, + my_global_state_ptr); + + // ----------------------------------------------- + // Update budgets + // ----------------------------------------------- + // Update budget (same for all agents) + if (ThisThreadAgentType == kConsumerType) { + my_state_arr[0] += cs_budget_delta; + } + if (ThisThreadAgentType == kFirmType) { + my_state_arr[0] += fm_budget_delta; + } + + // Add interest rate on savings + if ((ThisThreadAgentType == kConsumerType) || + (ThisThreadAgentType == kFirmType)) { + if (my_state_arr[0] > 0.0) { + my_state_arr[0] += my_state_arr[0] * kInterestRate; + } + } + + // Add new capital + if (ThisThreadAgentType == kFirmType) { + my_state_arr[kIdxFirmCapitalOffset] += capital_delta; + } + + // Add new capital + if ((ThisThreadAgentType == kFirmType) || + (ThisThreadAgentType == kGovernmentType)) { + *batch_action_value_ptr = *my_action_value_ptr; + } + + // Update rewards in global state + *my_reward_value_ptr = this_agent_reward; + *batch_reward_value_ptr = this_agent_reward; +} + +// ************************ +// End of extern "C" block. +// ************************ +} diff --git a/ai_economist/real_business_cycle/rbc/cuda_manager.py b/ai_economist/real_business_cycle/rbc/cuda_manager.py new file mode 100644 index 0000000..52b4503 --- /dev/null +++ b/ai_economist/real_business_cycle/rbc/cuda_manager.py @@ -0,0 +1,1930 @@ +# Copyright (c) 2021, salesforce.com, inc. +# All rights reserved. +# SPDX-License-Identifier: BSD-3-Clause +# For full license text, see the LICENSE file in the repo root +# or https://opensource.org/licenses/BSD-3-Clause + +import itertools +import os +import random +from pathlib import Path + +import numpy as np +import pycuda +import pycuda.autoinit +import pycuda.driver as cuda_driver +import scipy +import scipy.stats +import torch +from pycuda.compiler import SourceModule +from torch.distributions import Categorical +from tqdm import tqdm + +from .constants import ( + consumer_state_scaling_factors, + firm_state_scaling_factors, + govt_state_scaling_factors, +) +from .networks import DeterministicPolicy, IndependentPolicyNet, PolicyNet +from .util import expand_to_digit_form, size_after_digit_expansion + +_NP_DTYPE = np.float32 + +# the below line is 'strangely' necessary to make PyTorch work with PyCUDA +pytorch_cuda_init_success = torch.cuda.FloatTensor(8) + + +# for opening source files within module +module_path = Path(__file__).parent + + +def interval_list_contains(interval_list, step): + for (lower, upper_non_inclusive) in interval_list: + if lower <= step < upper_non_inclusive: + return True + return False + + +class NoOpOptimizer: + """ + Dummy Optimizer. + """ + + def __init__(self): + pass + + def step(self): + pass + + +def seed_everything(seed): + torch.manual_seed(seed) + random.seed(seed) + np.random.seed(seed) + + +def reverse_cumsum(x): + # assumes summing along episode iteration dim + return x + torch.sum(x, dim=-2, keepdims=True) - torch.cumsum(x, dim=-2) + + +def discounted_returns(rewards, gamma): + maxt = rewards.shape[-2] + cumulative_rewards = 0 + returns = torch.zeros_like(rewards) + for t in reversed(range(maxt)): + returns[:, t, :] = rewards[:, t, :] + gamma * cumulative_rewards + cumulative_rewards = rewards[:, t, :] + cumulative_rewards + return returns + + +def compute_theta_coef(hparams_dict, episode): + anneal_dict = hparams_dict["agents"]["consumer_anneal_theta"] + if anneal_dict["anneal_on"]: + exp_decay_length_in_steps = anneal_dict["exp_decay_length_in_steps"] + theta_coef = np.float32(1.0 - (np.exp(-episode / exp_decay_length_in_steps))) + else: + return np.float32(1.0) + return theta_coef + + +def government_action_mask(hparams_dict, step): + government_actions_array = hparams_dict["agents"]["government_actions_array"] + tax_annealing_params = hparams_dict["agents"]["government_anneal_taxes"] + + income_tax = torch.tensor(government_actions_array[:, 0]).cuda() + corporate_tax = torch.tensor(government_actions_array[:, 1]).cuda() + mask = torch.zeros(income_tax.shape[0]).cuda() + + if not tax_annealing_params["anneal_on"]: + return None + a0 = tax_annealing_params["start"] + max_tax = tax_annealing_params["increase_const"] * step + a0 + mask[(income_tax > max_tax) | (corporate_tax > max_tax)] -= 1000.0 + + return mask + + +def firm_action_mask(hparams_dict, step): + # pick out all firm actions where wage is the wrong height, + # and assign -1000.0 to those + firm_actions_array = hparams_dict["agents"]["firm_actions_array"] + wage_annealing_params = hparams_dict["agents"]["firm_anneal_wages"] + price_annealing_params = hparams_dict["agents"]["firm_anneal_prices"] + wages = torch.tensor(firm_actions_array[:, 1]).cuda() + prices = torch.tensor(firm_actions_array[:, 0]).cuda() + mask = torch.zeros(wages.shape[0]).cuda() + + if not (wage_annealing_params["anneal_on"] or price_annealing_params["anneal_on"]): + return None + + if wage_annealing_params["anneal_on"]: + a0 = wage_annealing_params["start"] + max_wage = wage_annealing_params["increase_const"] * step + a0 + min_wage = -wage_annealing_params["decrease_const"] * step + a0 + mask[(wages < min_wage) | (wages > max_wage)] -= 1000.0 + if price_annealing_params["anneal_on"]: + a0 = price_annealing_params["start"] + max_price = price_annealing_params["increase_const"] * step + a0 + min_price = -price_annealing_params["decrease_const"] * step + a0 + mask[(prices < min_price) | (prices > max_price)] -= 1000.0 + + return mask + + +def get_cuda_code(rel_path_to_cu_file, **preprocessor_vars_to_replace): + with open(module_path / rel_path_to_cu_file) as cudasource: + code_string = cudasource.read() + + # format for preprocessor macros in firm_rbc.cu is M_VARNAME. + # Specify all these as args to nvcc. + options_list = [ + f"-D M_{k.upper()}={v}" for k, v in preprocessor_vars_to_replace.items() + ] + + return code_string, options_list + + +def add_penalty_for_no_ponzi( + states, rewards, budget_offset, penalty_coef=20.0, penalty_scale=100.0 +): + budget_violations = -torch.clamp_max(states[..., budget_offset], 0.0) + rewards[:, -1, :] -= penalty_coef * budget_violations / penalty_scale + + +def update_government_rewards( + government_rewards, consumer_rewards, firm_rewards, cfg_dict +): + assert ( + government_rewards == 0.0 + ).all() # govt should have been assigned exactly 0 in cuda step function + total_rewards = consumer_rewards.sum(dim=-1) + if cfg_dict["agents"]["government_counts_firm_reward"] == 1: + total_rewards = total_rewards + cfg_dict["agents"].get( + "firm_reward_for_government_factor", 1.0 + ) * firm_rewards.sum(dim=-1) + + government_rewards[..., 0] = total_rewards[:] # one govt for now + + +def update_penalty_coef( + states, + budget_offset, + prev_penalty_coef, + penalty_step_size=0.01, + penalty_scale=100.0, +): + budget_violations = -torch.clamp_max(states[..., budget_offset], 0.0) + new_penalty_coef = ( + prev_penalty_coef + + penalty_step_size * (budget_violations / penalty_scale).mean().item() + ) + return new_penalty_coef + + +def get_actions_from_inds(action_inds, agents_dict): + + _action_inds = action_inds.cpu().to(torch.long) + + consumption_action_tensor = torch.tensor( + agents_dict["consumer_consumption_actions_array"] + ) + + work_action_tensor = torch.tensor(agents_dict["consumer_work_actions_array"]) + num_firms = agents_dict["num_firms"] + out_shape = _action_inds.shape[:-1] + (agents_dict["consumer_action_dim"],) + consumer_actions_out = torch.zeros(out_shape) + idx_hours_worked = num_firms + idx_which_firm = num_firms + 1 + + for i in range(num_firms): + consumer_actions_out[..., i] = consumption_action_tensor[ + _action_inds[..., i], : + ].squeeze(dim=-1) + + consumer_actions_out[..., num_firms] = work_action_tensor[ + _action_inds[..., idx_hours_worked], : + ].squeeze(dim=-1) + + consumer_actions_out[..., (num_firms + 1)] = _action_inds[..., idx_which_firm] + + return consumer_actions_out + + +def anneal_entropy_coef(entropy_dict, step): + if entropy_dict is None: + return 1.0 + + if entropy_dict["anneal_on"]: + coef_floor = entropy_dict.get("coef_floor", 0.0) + return max( + np.exp(-step / entropy_dict["exp_decay_length_in_steps"]), coef_floor + ) + return 1.0 + + +def get_grad_norm(policy): + grad_norm = 0.0 + for p in list(filter(lambda p: p.grad is not None, policy.parameters())): + grad_norm += (p.grad.data.norm(2).item()) ** 2 + return grad_norm + + +def get_ev(adv, returns, cutoff=-1.0): + return max(cutoff, (1 - (adv.detach().var() / returns.detach().var())).item()) + + +def consumer_ppo_step( + policy, + states, + actions, + rewards, + optimizer, + gamma_const, + entropy_val=0.0, + value_loss_weight=1.0, + ppo_num_updates=3, + reward_scale=1.0, + clip_grad_norm=None, + clip_param=0.1, +): + # Get initial policy predictions + multi_action_probs, old_value_preds = policy(states) + + old_value_preds = old_value_preds.detach() + # Get returns + rescaled_rewards = rewards / reward_scale + G_discounted_returns = discounted_returns(rescaled_rewards, gamma_const) + + # Value function loss + + sum_old_log_probs = 0.0 + for action_ind, probs in enumerate(multi_action_probs): + _CategoricalDist = Categorical(probs) + sum_old_log_probs += -1.0 * _CategoricalDist.log_prob(actions[..., action_ind]) + sum_old_log_probs = sum_old_log_probs.detach() + + assert not G_discounted_returns.requires_grad + assert not sum_old_log_probs.requires_grad + assert not old_value_preds.requires_grad + + # Compute ppo loss + for _ in range(ppo_num_updates): + multi_action_probs, value_preds = policy(states) + get_huber_loss = torch.nn.SmoothL1Loss() + value_pred_clipped = old_value_preds + (value_preds - old_value_preds).clamp( + -clip_param, clip_param + ) + value_loss_new = get_huber_loss( + value_preds.squeeze(dim=-1), G_discounted_returns + ) # can use huber loss instead + value_loss_clipped = get_huber_loss( + value_pred_clipped.squeeze(dim=-1), G_discounted_returns + ) + + value_loss = torch.max(value_loss_new, value_loss_clipped).mean() + + # Policy loss with value function baseline. + advantages = G_discounted_returns - value_preds.detach().squeeze(dim=-1) + # Don't propagate through to VF network. + assert not advantages.requires_grad + + # Trick: standardize advantages + standardized_advantages = (advantages - advantages.mean()) / ( + advantages.std() + 1e-6 + ) + sum_mean_entropy = 0.0 # mean over batch and agents + sum_neg_log_probs = 0.0 + + for action_ind, probs in enumerate(multi_action_probs): + _CategoricalDist = Categorical(probs) + sum_neg_log_probs += -1.0 * _CategoricalDist.log_prob( + actions[..., action_ind] + ) + sum_mean_entropy += _CategoricalDist.entropy().mean() + + assert sum_neg_log_probs.requires_grad + # note: log probs are negative, so negate again here + ratio = torch.exp(-sum_neg_log_probs + sum_old_log_probs) + surr1 = ratio * standardized_advantages + surr2 = ( + torch.clamp(ratio, 1.0 - clip_param, 1.0 + clip_param) + * standardized_advantages + ) + + ppo_loss = -torch.min(surr1, surr2).mean() + + loss = ( + ppo_loss - entropy_val * sum_mean_entropy + value_loss_weight * value_loss + ) + + # Apply gradients + optimizer.zero_grad() + loss.backward() + + if clip_grad_norm is not None: + torch.nn.utils.clip_grad_norm_(policy.parameters(), max_norm=clip_grad_norm) + + optimizer.step() + + +def ppo_step( + policy, + states, + actions, + rewards, + optimizer, + gamma_const, + entropy_val=0.0, + value_loss_weight=1.0, + ppo_num_updates=3, + actions_mask=None, + reward_scale=1.0, + clip_grad_norm=None, + clip_param=0.1, +): + # Get initial policy predictions + probs, old_value_preds = policy(states, actions_mask=actions_mask) + old_value_preds = old_value_preds.detach() + + # Get returns + rescaled_rewards = rewards / reward_scale + G_discounted_returns = discounted_returns(rescaled_rewards, gamma_const) + + # Value function loss + + _CategoricalDist = Categorical(probs) + old_log_probs = -1.0 * _CategoricalDist.log_prob(actions).detach() + + assert not G_discounted_returns.requires_grad + assert not old_log_probs.requires_grad + assert not old_value_preds.requires_grad + + # Compute ppo loss + for _ in range(ppo_num_updates): + probs, value_preds = policy(states, actions_mask=actions_mask) + get_huber_loss = torch.nn.SmoothL1Loss() + value_pred_clipped = old_value_preds + (value_preds - old_value_preds).clamp( + -clip_param, clip_param + ) + value_loss_new = get_huber_loss( + value_preds.squeeze(dim=-1), G_discounted_returns + ) # can use huber loss instead + value_loss_clipped = get_huber_loss( + value_pred_clipped.squeeze(dim=-1), G_discounted_returns + ) + + value_loss = torch.max(value_loss_new, value_loss_clipped).mean() + + # Policy loss with value function baseline. + advantages = G_discounted_returns - value_preds.detach().squeeze(dim=-1) + # Don't propagate through to VF network. + assert not advantages.requires_grad + + # Trick: standardize advantages + standardized_advantages = (advantages - advantages.mean()) / ( + advantages.std() + 1e-6 + ) + + _CategoricalDist = Categorical(probs) + neg_log_probs = -1.0 * _CategoricalDist.log_prob(actions) + mean_entropy = _CategoricalDist.entropy().mean() + + assert neg_log_probs.requires_grad + # note: log probs are negative, so negate again here + ratio = torch.exp(-neg_log_probs + old_log_probs) + surr1 = ratio * standardized_advantages + surr2 = ( + torch.clamp(ratio, 1.0 - clip_param, 1.0 + clip_param) + * standardized_advantages + ) + + ppo_loss = -torch.min(surr1, surr2).mean() + + loss = ppo_loss - entropy_val * mean_entropy + value_loss_weight * value_loss + + # Apply gradients + optimizer.zero_grad() + loss.backward() + + if clip_grad_norm is not None: + torch.nn.utils.clip_grad_norm_(policy.parameters(), max_norm=clip_grad_norm) + + optimizer.step() + + +def consumer_policy_gradient_step( + policy, + states, + actions, + rewards, + optimizer, + gamma_const, + entropy_val=0.0, + value_loss_weight=1.0, + reward_scale=1.0, + clip_grad_norm=None, +): + # Get policy and value predictions + multi_action_probs, value_preds = policy(states) + + # Get returns + rescaled_rewards = rewards / reward_scale + G_discounted_returns = discounted_returns(rescaled_rewards, gamma_const) + + # Value function loss + get_huber_loss = torch.nn.SmoothL1Loss() + value_loss = get_huber_loss( + value_preds.squeeze(dim=-1), G_discounted_returns + ).mean() # can use huber loss instead + + # Policy loss with value function baseline. + advantages = G_discounted_returns - value_preds.detach().squeeze(dim=-1) + # Don't propagate through to VF network. + assert not advantages.requires_grad + + # Trick: standardize advantages + standardized_advantages = (advantages - advantages.mean()) / ( + advantages.std() + 1e-6 + ) + + # Compute policy loss + sum_mean_entropy = 0.0 # mean over batch and agents + sum_neg_log_probs = 0.0 + + for action_ind, probs in enumerate(multi_action_probs): + _CategoricalDist = Categorical(probs) + sum_neg_log_probs += -1.0 * _CategoricalDist.log_prob(actions[..., action_ind]) + sum_mean_entropy += _CategoricalDist.entropy().mean() + + pg_loss = (sum_neg_log_probs * standardized_advantages).mean() + assert sum_neg_log_probs.requires_grad + + loss = pg_loss - entropy_val * sum_mean_entropy + value_loss_weight * value_loss + + # Apply gradients + optimizer.zero_grad() + loss.backward() + + if clip_grad_norm is not None: + torch.nn.utils.clip_grad_norm_(policy.parameters(), max_norm=clip_grad_norm) + + optimizer.step() + + +def policy_gradient_step( + policy, + states, + actions, + rewards, + optimizer, + gamma_const, + entropy_val=0.0, + value_loss_weight=1.0, + actions_mask=None, + reward_scale=1.0, + clip_grad_norm=None, +): + + # here, we must perform digit scaling + optimizer.zero_grad() + probs, value_preds = policy(states, actions_mask=actions_mask) + rewards = rewards / reward_scale + G_discounted_returns = discounted_returns(rewards, gamma_const) + get_huber_loss = torch.nn.SmoothL1Loss() + value_loss = get_huber_loss( + value_preds.squeeze(dim=-1), G_discounted_returns + ).mean() # can use huber loss instead + advantages = G_discounted_returns - value_preds.detach().squeeze( + dim=-1 + ) # compute advantages (don't propagate through to VF network) + assert not advantages.requires_grad + # mean and standardize advantages + standardized_advantages = (advantages - advantages.mean()) / ( + advantages.std() + 1e-6 + ) + assert not standardized_advantages.requires_grad + m = Categorical(probs) + pg_loss = (-m.log_prob(actions) * standardized_advantages).mean() + assert pg_loss.requires_grad + entropy_regularize = entropy_val * m.entropy().mean() + loss = pg_loss - entropy_regularize + value_loss_weight * value_loss + loss.backward() + + if clip_grad_norm is not None: + torch.nn.utils.clip_grad_norm_(policy.parameters(), max_norm=clip_grad_norm) + + optimizer.step() + + +def save_dense_log( + save_dir, + epi, + agent_type_arrays, + agent_action_arrays, + agent_aux_arrays, +): + print(f"Saving dense log at episode {epi}") + for agent_type in ["consumer", "firm", "government"]: + states_batch, actions_batch, rewards_batch = agent_type_arrays[agent_type] + aux_array = agent_aux_arrays[agent_type] + if aux_array is not None: + aux_array = aux_array.cpu().numpy() + np.savez( + str(Path(save_dir) / Path(f"episode_{epi}_{agent_type}.npz")), + states=states_batch.cpu().numpy(), + actions=actions_batch.cpu().numpy(), + rewards=rewards_batch.cpu().numpy(), + action_array=agent_action_arrays[agent_type], + aux_array=aux_array, + ) + + +def save_policy_parameters( + save_dir, + epi, + consumer_policy, + firm_policy, + government_policy, + freeze_firms, + freeze_govt, +): + print(f"saving model parameters at episode {epi}") + consumer_path = ( + Path(save_dir) / Path("saved_models") / Path(f"consumer_policy_{epi}.pt") + ) + + # always save the latest, to be overwritten later + consumer_path_latest = ( + Path(save_dir) / Path("saved_models") / Path("consumer_policy_latest.pt") + ) + os.makedirs(consumer_path.parent, exist_ok=True) + torch.save(consumer_policy.state_dict(), consumer_path) + torch.save(consumer_policy.state_dict(), consumer_path_latest) + + if freeze_firms is None: + firm_path = ( + Path(save_dir) / Path("saved_models") / Path(f"firm_policy_{epi}.pt") + ) + firm_path_latest = ( + Path(save_dir) / Path("saved_models") / Path("firm_policy_latest.pt") + ) + + os.makedirs(firm_path.parent, exist_ok=True) + torch.save(firm_policy.state_dict(), firm_path) + torch.save(firm_policy.state_dict(), firm_path_latest) + if freeze_govt is None: + government_path = ( + Path(save_dir) / Path("saved_models") / Path(f"government_policy_{epi}.pt") + ) + government_path_latest = ( + Path(save_dir) / Path("saved_models") / Path("government_policy_latest.pt") + ) + + os.makedirs(government_path.parent, exist_ok=True) + torch.save(government_policy.state_dict(), government_path) + torch.save(government_policy.state_dict(), government_path_latest) + + +class ConsumerFirmRunManagerBatchParallel: + """ + The Real Business Cycle Experiment Management Class. + """ + + def __init__(self, cfg_dict, freeze_firms=None, freeze_govt=None): + self.cfg_dict = cfg_dict + self.train_dict = cfg_dict["train"] + self.agents_dict = cfg_dict["agents"] + self.world_dict = cfg_dict["world"] + self.save_dense_every = self.train_dict["save_dense_every"] + self.save_dir = self.train_dict["save_dir"] + + self.freeze_firms = freeze_firms + self.freeze_govt = freeze_govt + + self.__init_cuda_functions() + self.__init_cuda_data_structs() + self.__init_torch_data() + + def __init_cuda_data_structs(self): + __td = self.train_dict + __ad = self.agents_dict + __wd = self.world_dict + batch_size = __td["batch_size"] + num_consumers = __ad["num_consumers"] + num_firms = __ad["num_firms"] + num_governments = __ad["num_governments"] + firm_action_dim = __ad["firm_action_dim"] + government_action_dim = __ad["government_action_dim"] + consumer_state_dim = __ad["consumer_state_dim"] + firm_state_dim = __ad["firm_state_dim"] + government_state_dim = __ad["government_state_dim"] + global_state_dim = __ad["global_state_dim"] + consumer_endowment = __wd["initial_consumer_endowment"] + firm_endowment = __wd["initial_firm_endowment"] + initial_stocks = __wd["initial_stocks"] + initial_wages = __wd["initial_wages"] + initial_prices = __wd["initial_prices"] + consumer_theta = __wd["consumer_theta"] + + consumer_rewards = np.zeros((batch_size, num_consumers), dtype=_NP_DTYPE) + consumer_states = np.zeros( + (batch_size, num_consumers, consumer_state_dim), dtype=_NP_DTYPE + ) + + firm_action_indices = np.zeros((batch_size, num_firms), dtype=np.int32) + firm_actions = np.zeros( + (batch_size, num_firms, firm_action_dim), dtype=_NP_DTYPE + ) + firm_rewards = np.zeros((batch_size, num_firms), dtype=_NP_DTYPE) + firm_states = np.zeros((batch_size, num_firms, firm_state_dim), dtype=_NP_DTYPE) + + government_action_indices = np.zeros( + (batch_size, num_governments), dtype=np.int32 + ) + government_actions = np.zeros( + (batch_size, num_governments, government_action_dim), dtype=_NP_DTYPE + ) + government_rewards = np.zeros((batch_size, num_governments), dtype=_NP_DTYPE) + government_states = np.zeros( + (batch_size, num_governments, government_state_dim), dtype=_NP_DTYPE + ) + + # initialize states to right values here + + # global state init + # for consumers, firms, and governments + for state_arr in [consumer_states, firm_states, government_states]: + # set prices to 1.0 + state_arr[:, :, 0:num_firms] = initial_prices + # set wages to 0.0 + state_arr[:, :, num_firms : (2 * num_firms)] = initial_wages + # set stocks to 0.0 + state_arr[:, :, (2 * num_firms) : (3 * num_firms)] = initial_stocks + # set goods overdemanded to 0.0 + state_arr[:, :, (3 * num_firms) : (4 * num_firms)] = 0.0 + # set taxes to 0.0 + state_arr[:, :, (4 * num_firms)] = 0.0 + state_arr[:, :, (4 * num_firms) + 1] = 0.0 + + # consumer states, set theta and initial budget + if "paretoscaletheta" in __wd: + pareto_vals = np.expand_dims( + scipy.stats.pareto.ppf( + (np.arange(num_consumers) / num_consumers), __wd["paretoscaletheta"] + ), + axis=0, + ) + consumer_states[:, :, consumer_state_dim - 1] = consumer_theta * ( + 1.0 / pareto_vals + ) + else: + consumer_states[:, :, consumer_state_dim - 1] = consumer_theta + consumer_states[:, :, global_state_dim] = consumer_endowment + + # firm states + # capital + if __wd.get("initial_capital", None) == "proportional": + for i in range(num_firms): + firm_states[:, i, global_state_dim + 1] = ((i + 1) / 10.0) * 2.0 + elif __wd.get("initial_capital", None) == "twolevel": + for i in range(num_firms): + if i < (num_firms // 2): + firm_states[:, i, global_state_dim + 1] = 5000 + else: + firm_states[:, i, global_state_dim + 1] = 10000 + else: + firm_states[:, :, global_state_dim + 1] = 1.0 + + # production alpha + if __wd["production_alpha"] == "proportional": + half_firms = num_firms // 2 + for i in range(num_firms): + firm_states[:, i, global_state_dim + 2] = ((i % half_firms) + 1) * 0.2 + elif __wd["production_alpha"] == "fixed_array": + alpha_arr = [0.2, 0.3, 0.4, 0.6, 0.8, 0.2, 0.3, 0.4, 0.6, 0.8] + for i in range(num_firms): + firm_states[:, i, global_state_dim + 2] = alpha_arr[i] + else: + for i in range(num_firms): + firm_states[:, i, global_state_dim + 2] = __wd["production_alpha"] + + # set one-hot fields correctly by index for each firm + onehot_rows = np.eye(num_firms) + firm_states[:, :, (global_state_dim + 3) :] = onehot_rows + firm_states[:, :, global_state_dim] = firm_endowment + + # government states + # for now, nothing beyond global state + + self.consumer_states_gpu_tensor = torch.from_numpy(consumer_states).cuda() + # these are now tensors bc sampling for consumers via pytorch + self.consumer_rewards_gpu_pycuda = cuda_driver.mem_alloc( + consumer_rewards.nbytes + ) + self.consumer_states_checkpoint_gpu_pycuda = cuda_driver.mem_alloc( + consumer_states.nbytes + ) + cuda_driver.memcpy_htod(self.consumer_rewards_gpu_pycuda, consumer_rewards) + cuda_driver.memcpy_htod( + self.consumer_states_checkpoint_gpu_pycuda, consumer_states + ) + + self.firm_states_gpu_tensor = torch.from_numpy(firm_states).cuda() + self.firm_action_indices_gpu_pycuda = cuda_driver.mem_alloc( + firm_action_indices.nbytes + ) + self.firm_actions_gpu_pycuda = cuda_driver.mem_alloc(firm_actions.nbytes) + self.firm_rewards_gpu_pycuda = cuda_driver.mem_alloc(firm_rewards.nbytes) + self.firm_states_checkpoint_gpu_pycuda = cuda_driver.mem_alloc( + firm_states.nbytes + ) + cuda_driver.memcpy_htod( + self.firm_action_indices_gpu_pycuda, firm_action_indices + ) + cuda_driver.memcpy_htod(self.firm_actions_gpu_pycuda, firm_actions) + cuda_driver.memcpy_htod(self.firm_rewards_gpu_pycuda, firm_rewards) + cuda_driver.memcpy_htod(self.firm_states_checkpoint_gpu_pycuda, firm_states) + + self.government_states_gpu_tensor = torch.from_numpy(government_states).cuda() + self.government_action_indices_gpu_pycuda = cuda_driver.mem_alloc( + government_action_indices.nbytes + ) + self.government_actions_gpu_pycuda = cuda_driver.mem_alloc( + government_actions.nbytes + ) + self.government_rewards_gpu_pycuda = cuda_driver.mem_alloc( + government_rewards.nbytes + ) + self.government_states_checkpoint_gpu_pycuda = cuda_driver.mem_alloc( + government_states.nbytes + ) + cuda_driver.memcpy_htod( + self.government_action_indices_gpu_pycuda, government_action_indices + ) + cuda_driver.memcpy_htod(self.government_actions_gpu_pycuda, government_actions) + cuda_driver.memcpy_htod(self.government_rewards_gpu_pycuda, government_rewards) + cuda_driver.memcpy_htod( + self.government_states_checkpoint_gpu_pycuda, government_states + ) + + def __init_torch_data(self): + + __td = self.train_dict + __ad = self.agents_dict + + batch_size = __td["batch_size"] + num_consumers = __ad["num_consumers"] + num_firms = __ad["num_firms"] + num_governments = __ad["num_governments"] + consumer_action_dim = __ad["consumer_action_dim"] + consumer_state_dim = __ad["consumer_state_dim"] + firm_state_dim = __ad["firm_state_dim"] + government_state_dim = __ad["government_state_dim"] + num_iters = int(self.world_dict["maxtime"]) + + consumer_states_batch = torch.zeros( + batch_size, + num_iters, + num_consumers, + consumer_state_dim, + dtype=torch.float32, + device="cpu", + ) + consumer_actions_single = torch.zeros( + batch_size, + num_consumers, + num_firms + 1 + 1, + dtype=torch.int32, + device="cpu", + ) + consumer_actions_batch = torch.zeros( + batch_size, + num_iters, + num_consumers, + num_firms + 1 + 1, + dtype=torch.int32, + device="cpu", + ) + + # auxiliary state info that is not part of observables. + # currently just the realized consumption + consumer_aux_batch = torch.zeros( + batch_size, + num_iters, + num_consumers, + num_firms, + dtype=torch.float32, + device="cpu", + ) + + consumer_rewards_batch = torch.zeros( + batch_size, num_iters, num_consumers, dtype=torch.float32, device="cpu" + ) + self.consumer_states_batch_gpu_tensor = consumer_states_batch.cuda() + self.consumer_actions_batch_gpu_tensor = consumer_actions_batch.cuda() + self.consumer_actions_index_single_gpu_tensor = consumer_actions_single.cuda() + self.consumer_actions_single_gpu_tensor = torch.zeros( + batch_size, + num_consumers, + consumer_action_dim, + dtype=torch.float32, + device="cpu", + ).cuda() + self.consumer_rewards_batch_gpu_tensor = consumer_rewards_batch.cuda() + self.consumer_aux_batch_gpu_tensor = consumer_aux_batch.cuda() + + firm_states_batch = torch.zeros( + batch_size, + num_iters, + num_firms, + firm_state_dim, + dtype=torch.float32, + device="cpu", + ) + firm_actions_batch = torch.zeros( + batch_size, num_iters, num_firms, dtype=torch.int32, device="cpu" + ) + firm_rewards_batch = torch.zeros( + batch_size, num_iters, num_firms, dtype=torch.float32, device="cpu" + ) + firm_aux_batch = torch.zeros( + batch_size, num_iters, num_firms, dtype=torch.float32, device="cpu" + ) + self.firm_states_batch = firm_states_batch.cuda() + self.firm_actions_batch = firm_actions_batch.cuda() + self.firm_rewards_batch = firm_rewards_batch.cuda() + self.firm_aux_batch = firm_aux_batch.cuda() + + government_states_batch = torch.zeros( + batch_size, + num_iters, + num_governments, + government_state_dim, + dtype=torch.float32, + device="cpu", + ) + government_actions_batch = torch.zeros( + batch_size, num_iters, num_governments, dtype=torch.int32, device="cpu" + ) + government_rewards_batch = torch.zeros( + batch_size, num_iters, num_governments, dtype=torch.float32, device="cpu" + ) + self.government_states_batch = government_states_batch.cuda() + self.government_actions_batch = government_actions_batch.cuda() + self.government_rewards_batch = government_rewards_batch.cuda() + + def __init_cuda_functions(self): + + __td = self.train_dict + __ad = self.agents_dict + __wd = self.world_dict + + if self.freeze_firms is not None: + countfirmreward = 0 + else: + countfirmreward = self.agents_dict["government_counts_firm_reward"] + + code, compiler_options = get_cuda_code( + Path("cuda") / Path("firm_rbc.cu"), + batchsize=__td["batch_size"], + numconsumers=__ad["num_consumers"], + numfirms=__ad["num_firms"], + numgovernments=__ad["num_governments"], + maxtime=__wd["maxtime"], + # numactionsconsumer=__ad["consumer_num_actions"], + numactionsconsumer=__ad["consumer_num_work_actions"], + numactionsfirm=__ad["firm_num_actions"], + numactionsgovernment=__ad["government_num_actions"], + interestrate=__wd["interest_rate"], + crra_param=__wd["crra_param"], + shouldboostfirmreward=int(__td["should_boost_firm_reward"]), + boostfirmrewardfactor=__td["boost_firm_reward_factor"], + countfirmreward=countfirmreward, + importerprice=__wd["importer_price"], + importerquantity=__wd["importer_quantity"], + laborfloor=__wd.get("labor_floor", 0.0), + useimporter=__wd["use_importer"], + ) + + mod = SourceModule(code, options=compiler_options, no_extern_c=True) + self.mod = mod + + # -------------------------------------------------------------------- + # Define Consumer actions -- maanged in Pytorch + # -------------------------------------------------------------------- + self.consumption_action_tensor = torch.tensor( + __ad["consumer_consumption_actions_array"].astype(_NP_DTYPE) + ).cuda() + self.work_action_tensor = torch.tensor( + __ad["consumer_work_actions_array"].astype(_NP_DTYPE) + ).cuda() + + # -------------------------------------------------------------------- + # Define Firm actions -- maanged in CUDA + # -------------------------------------------------------------------- + firm_index_to_action_gpu, _ = mod.get_global("kFirmIndexToAction") + cuda_driver.memcpy_htod( + firm_index_to_action_gpu, + __ad["firm_actions_array"].astype(_NP_DTYPE), + ) + + # -------------------------------------------------------------------- + # Define Govt actions -- maanged in CUDA + # -------------------------------------------------------------------- + government_index_to_action_gpu, _ = mod.get_global("kGovernmentIndexToAction") + cuda_driver.memcpy_htod( + government_index_to_action_gpu, + __ad["government_actions_array"].astype(_NP_DTYPE), + ) + + # -------------------------------------------------------------------- + # Get handles to CUDA methods + # -------------------------------------------------------------------- + self.cuda_init_random = mod.get_function("CudaInitKernel") + self.cuda_reset_env = mod.get_function("CudaResetEnv") + self.cuda_sample_actions = mod.get_function( + "CudaSampleFirmAndGovernmentActions" + ) + self.cuda_step = mod.get_function("CudaStep") + self.cuda_free_mem = mod.get_function("CudaFreeRand") + + def _update_consumer_actions_inplace(self): + # call after consumer_actions_single is updated + __ad = self.agents_dict + + # Add asserts when ``loading'' arrays + # assert consumption_action_array.shape == (1, 1, 1) + # assert len(consumption_action_array.shape) == 3 + + num_firms = __ad["num_firms"] + idx_hours = num_firms + idx_which_firm = num_firms + 1 + for i in range(num_firms): + + consumption_actions_at_firm_i = ( + self.consumer_actions_index_single_gpu_tensor[..., i].to(torch.long) + ) + + self.consumer_actions_single_gpu_tensor[ + ..., i + ] = self.consumption_action_tensor[ + consumption_actions_at_firm_i, : + ].squeeze( + dim=-1 + ) + + consumer_hours_worked = self.consumer_actions_index_single_gpu_tensor[ + ..., idx_hours + ].to(torch.long) + + self.consumer_actions_single_gpu_tensor[ + ..., num_firms + ] = self.work_action_tensor[consumer_hours_worked, :].squeeze(dim=-1) + + self.consumer_actions_single_gpu_tensor[ + ..., num_firms + 1 + ] = self.consumer_actions_index_single_gpu_tensor[..., idx_which_firm] + + def sample_consumer_actions_and_store(self, consumer_probs_list): + # Every consumer has A action heads, output as a list of tensors. + # Sample from each of these lists and store the results. + + with torch.no_grad(): + for i, probs in enumerate(consumer_probs_list): + dist = Categorical(probs) + samples = dist.sample() + self.consumer_actions_index_single_gpu_tensor[..., i] = samples + + self._update_consumer_actions_inplace() + + def consumers_will_train_this_episode(self, epi): + __ad = self.agents_dict + if "training_schedule_mod" in self.agents_dict: + mod_val = epi % __ad["training_schedule_mod"] + return mod_val <= __ad["consumer_mod_threshold"] + if "consumer_training_list" in self.agents_dict: + return interval_list_contains(__ad["consumer_training_list"], epi) + if "train_consumers_every" in self.agents_dict: + mod_val = epi % __ad["train_consumers_every"] + else: + mod_val = 0 + return epi >= self.agents_dict.get("consumer_training_start", 0) and ( + mod_val == 0 + ) + + def firms_will_train_this_episode(self, epi): + __ad = self.agents_dict + if "training_schedule_mod" in self.agents_dict: + mod_val = epi % __ad["training_schedule_mod"] + return mod_val > __ad["consumer_mod_threshold"] + if "firm_training_list" in self.agents_dict: + return interval_list_contains(__ad["firm_training_list"], epi) and ( + self.freeze_firms is None + ) + if "train_firms_every" in self.agents_dict: + mod_val = epi % __ad["train_firms_every"] + else: + mod_val = 0 + return ( + (epi >= self.agents_dict.get("firm_training_start", 0)) + and (self.freeze_firms is None) + and (mod_val == 0) + ) + + def governments_will_train_this_episode(self, epi): + __ad = self.agents_dict + if "government_training_list" in self.agents_dict: + return interval_list_contains(__ad["government_training_list"], epi) and ( + self.freeze_govt is None + ) + if "train_government_every" in self.agents_dict: + mod_val = epi % self.agents_dict["train_government_every"] + else: + mod_val = 0 + return ( + (epi >= self.agents_dict.get("government_training_start", 0)) + and (self.freeze_govt is None) + and (mod_val == 0) + ) + + def bestresponse_train( + self, train_type, num_episodes, rollout_path, ep_str="latest", checkpoint=100 + ): + # train one single type only + # load all policies from state dict + # reset all the environment stuff + + __td = self.train_dict + __ad = self.agents_dict + num_iters = int(self.world_dict["maxtime"]) + num_consumers = __ad["num_consumers"] + num_firms = __ad["num_firms"] + num_governments = __ad["num_governments"] + num_agents = num_consumers + num_firms + num_governments + block = (num_agents, 1, 1) + grid = (__td["batch_size"], 1) + + seed_everything(__td["seed"]) + self.cuda_init_random(np.int32(__td["seed"]), block=block, grid=grid) + + # -------------------------------------------- + # Define Consumer policy + optimizers + # -------------------------------------------- + lr = __td["lr"] + + consumer_expanded_size = size_after_digit_expansion( + __ad["consumer_state_dim"], + __ad["consumer_digit_dims"], + __td["digit_representation_size"], + ) + + consumer_policy = IndependentPolicyNet( + consumer_expanded_size, + [__ad["consumer_num_consume_actions"]] * num_firms + + [ + __ad["consumer_num_work_actions"], + __ad["consumer_num_whichfirm_actions"], + ], + norm_consts=( + torch.zeros(consumer_expanded_size).cuda(), # don't center for now + consumer_state_scaling_factors(self.cfg_dict), + ), + ).to("cuda") + consumer_policy.load_state_dict( + torch.load( + rollout_path + / Path("saved_models") + / Path(f"consumer_policy_{ep_str}.pt") + ) + ) + + consumer_optim = torch.optim.Adam(consumer_policy.parameters(), lr=lr) + firm_expanded_size = size_after_digit_expansion( + __ad["firm_state_dim"], + __ad["firm_digit_dims"], + __td["digit_representation_size"], + ) + firm_policy = PolicyNet( + firm_expanded_size, + __ad["firm_num_actions"], + norm_consts=( + torch.zeros(firm_expanded_size).cuda(), + firm_state_scaling_factors(self.cfg_dict), + ), + ).to("cuda") + + firm_policy.load_state_dict( + torch.load( + rollout_path / Path("saved_models") / Path(f"firm_policy_{ep_str}.pt") + ) + ) + + firm_optim = torch.optim.Adam(firm_policy.parameters(), lr=lr) + government_expanded_size = size_after_digit_expansion( + __ad["government_state_dim"], + __ad["government_digit_dims"], + __td["digit_representation_size"], + ) + + government_policy = PolicyNet( + government_expanded_size, + __ad["government_num_actions"], + norm_consts=( + torch.zeros(government_expanded_size).cuda(), + govt_state_scaling_factors(self.cfg_dict), + ), + ).to("cuda") + + government_policy.load_state_dict( + torch.load( + rollout_path + / Path("saved_models") + / Path(f"government_policy_{ep_str}.pt") + ) + ) + + government_optim = torch.optim.Adam(government_policy.parameters(), lr=lr) + rewards = [] + + agent_type_arrays = { + "consumer": ( + self.consumer_states_batch_gpu_tensor, + self.consumer_actions_batch_gpu_tensor, + self.consumer_rewards_batch_gpu_tensor, + ), + "firm": ( + self.firm_states_batch, + self.firm_actions_batch, + self.firm_rewards_batch, + ), + "government": ( + self.government_states_batch, + self.government_actions_batch, + self.government_rewards_batch, + ), + } + + agent_action_arrays = { + "consumer": __ad["consumer_work_actions_array"], + "firm": __ad["firm_actions_array"], + "government": __ad["government_actions_array"], + } + + agent_aux_arrays = { + "consumer": (self.consumer_aux_batch_gpu_tensor), + "firm": (self.firm_aux_batch), + "government": None, + } + + pbar = tqdm(range(num_episodes)) + for epi in pbar: + annealed_entropy_coef = 0.1 # later, do some computation to anneal this + self.cuda_reset_env( + CudaTensorHolder(self.consumer_states_gpu_tensor), + CudaTensorHolder(self.firm_states_gpu_tensor), + CudaTensorHolder(self.government_states_gpu_tensor), + self.consumer_states_checkpoint_gpu_pycuda, + self.firm_states_checkpoint_gpu_pycuda, + self.government_states_checkpoint_gpu_pycuda, + np.float32(1.0), + block=block, + grid=grid, + ) + + for _iter in range(num_iters): + + # ------------------------ + # Run policy and get probs + # ------------------------ + with torch.no_grad(): + # here, we must perform digit scaling + consumer_probs_list, _ = consumer_policy( + expand_to_digit_form( + self.consumer_states_gpu_tensor, + __ad["consumer_digit_dims"], + __td["digit_representation_size"], + ) + ) + firm_probs, _ = firm_policy( + expand_to_digit_form( + self.firm_states_gpu_tensor, + __ad["firm_digit_dims"], + __td["digit_representation_size"], + ), + actions_mask=None, + ) + government_probs, _ = government_policy( + expand_to_digit_form( + self.government_states_gpu_tensor, + __ad["government_digit_dims"], + __td["digit_representation_size"], + ), + actions_mask=None, + ) + + # ------------------------ + # Get action samples + # ------------------------ + # Sample consumer actions using PyTorch here on GPU! + self.sample_consumer_actions_and_store(consumer_probs_list) + + # Sample firms + govt actions using PyCUDA on GPU! + self.cuda_sample_actions( + CudaTensorHolder(firm_probs), + self.firm_action_indices_gpu_pycuda, + self.firm_actions_gpu_pycuda, + CudaTensorHolder(government_probs), + self.government_action_indices_gpu_pycuda, + self.government_actions_gpu_pycuda, + block=block, + grid=grid, + ) + + # ------------------------ + # Step on GPU + # ------------------------ + self.cuda_step( + CudaTensorHolder( + # size: batches x n_consumers x consumer_state float + self.consumer_states_gpu_tensor + ), + CudaTensorHolder( + # size: batches x n_consumers x consumer_action_dim float + self.consumer_actions_single_gpu_tensor + ), + # size: batches x n_consumers x 1 float + self.consumer_rewards_gpu_pycuda, + CudaTensorHolder( + self.consumer_states_batch_gpu_tensor + ), # size: batches x episode x n_consumers x consumer_state float + CudaTensorHolder(self.consumer_rewards_batch_gpu_tensor), + CudaTensorHolder(self.firm_states_gpu_tensor), + self.firm_action_indices_gpu_pycuda, + self.firm_actions_gpu_pycuda, + self.firm_rewards_gpu_pycuda, + CudaTensorHolder(self.firm_states_batch), + CudaTensorHolder(self.firm_actions_batch), + CudaTensorHolder(self.firm_rewards_batch), + CudaTensorHolder(self.government_states_gpu_tensor), + self.government_action_indices_gpu_pycuda, + self.government_actions_gpu_pycuda, + self.government_rewards_gpu_pycuda, + CudaTensorHolder(self.government_states_batch), + CudaTensorHolder(self.government_actions_batch), + CudaTensorHolder(self.government_rewards_batch), + CudaTensorHolder(self.consumer_aux_batch_gpu_tensor), + CudaTensorHolder(self.firm_aux_batch), + np.int32(_iter), + block=block, + grid=grid, + ) + self.consumer_actions_batch_gpu_tensor[ + :, _iter, :, : + ] = self.consumer_actions_index_single_gpu_tensor + update_government_rewards( + self.government_rewards_batch, + self.consumer_rewards_batch_gpu_tensor, + self.firm_rewards_batch, + self.cfg_dict, + ) + if train_type == "consumer": + consumer_reward_scale = self.agents_dict.get( + "consumer_reward_scale", 1.0 + ) + consumer_policy_gradient_step( + consumer_policy, + expand_to_digit_form( + self.consumer_states_batch_gpu_tensor, + __ad["consumer_digit_dims"], + __td["digit_representation_size"], + ), + self.consumer_actions_batch_gpu_tensor, + self.consumer_rewards_batch_gpu_tensor, + consumer_optim, + __td["gamma"], + entropy_val=annealed_entropy_coef * __td["entropy"], + value_loss_weight=__td["value_loss_weight"], + reward_scale=consumer_reward_scale, + clip_grad_norm=self.train_dict.get("clip_grad_norm", None), + ) + rewards.append(self.consumer_rewards_batch_gpu_tensor.mean().item()) + elif train_type == "firm": + firm_reward_scale = self.agents_dict.get("firm_reward_scale", 1.0) + policy_gradient_step( + firm_policy, + expand_to_digit_form( + self.firm_states_batch, + __ad["firm_digit_dims"], + __td["digit_representation_size"], + ), + self.firm_actions_batch, + self.firm_rewards_batch, + firm_optim, + __td["gamma"], + entropy_val=annealed_entropy_coef * __td["entropy"], + value_loss_weight=__td["value_loss_weight"], + actions_mask=None, + reward_scale=firm_reward_scale, + clip_grad_norm=self.train_dict.get("clip_grad_norm", None), + ) + rewards.append(self.firm_rewards_batch.mean().item()) + elif train_type == "government": + government_reward_scale = self.agents_dict.get( + "government_reward_scale", 1.0 + ) + policy_gradient_step( + government_policy, + expand_to_digit_form( + self.government_states_batch, + __ad["government_digit_dims"], + __td["digit_representation_size"], + ), + self.government_actions_batch, + self.government_rewards_batch, + government_optim, + __td["gamma"], + entropy_val=annealed_entropy_coef * __td["entropy"], + value_loss_weight=__td["value_loss_weight"], + actions_mask=None, + reward_scale=government_reward_scale, + clip_grad_norm=self.train_dict.get("clip_grad_norm", None), + ) + rewards.append(self.government_rewards_batch.mean().item()) + pbar.set_postfix({"reward": rewards[-1]}) + if (epi % checkpoint) == 0: + # save policy every checkpoint steps + save_policy_parameters( + str(Path(self.save_dir) / f"br{train_type}"), + epi, + consumer_policy, + firm_policy, + government_policy, + self.freeze_firms, + self.freeze_govt, + ) + save_dense_log( + str(Path(self.save_dir) / f"br{train_type}"), + epi, + agent_type_arrays, + agent_action_arrays, + agent_aux_arrays, + ) + + print( + f"{train_type}: starting reward {rewards[0]}, " + f"ending reward {rewards[-1]}, " + f"improvement in reward after {num_episodes}: {rewards[-1] - rewards[0]}" + ) + + self.cuda_free_mem(block=block, grid=grid) + return rewards + + def train(self): + + __td = self.train_dict + __ad = self.agents_dict + + # Create logdir + os.makedirs(__td["save_dir"], exist_ok=True) + + # Constants + num_iters = int(self.world_dict["maxtime"]) + num_consumers = __ad["num_consumers"] + num_firms = __ad["num_firms"] + num_governments = __ad["num_governments"] + num_agents = num_consumers + num_firms + num_governments + + # CUDA params: defines data shape on the GPU + block = (num_agents, 1, 1) + grid = (__td["batch_size"], 1) + + # Set seeds + seed_everything(__td["seed"]) + self.cuda_init_random(np.int32(__td["seed"]), block=block, grid=grid) + + # -------------------------------------------- + # Define Consumer policy + optimizers + # -------------------------------------------- + lr = __td["lr"] + + consumer_expanded_size = size_after_digit_expansion( + __ad["consumer_state_dim"], + __ad["consumer_digit_dims"], + __td["digit_representation_size"], + ) + + consumer_policy = IndependentPolicyNet( + consumer_expanded_size, + [__ad["consumer_num_consume_actions"]] * num_firms + + [ + __ad["consumer_num_work_actions"], + __ad["consumer_num_whichfirm_actions"], + ], + norm_consts=( + torch.zeros(consumer_expanded_size).cuda(), # don't center for now + consumer_state_scaling_factors(self.cfg_dict), + ), + ).to("cuda") + + consumer_optim = torch.optim.Adam( + consumer_policy.parameters(), + lr=lr * self.agents_dict.get("consumer_lr_multiple", 1.0), + ) + + # -------------------------------------------- + # Define Firm policy + optimizers + # -------------------------------------------- + firm_expanded_size = size_after_digit_expansion( + __ad["firm_state_dim"], + __ad["firm_digit_dims"], + __td["digit_representation_size"], + ) + + if self.freeze_firms is not None: + firm_policy = DeterministicPolicy( + firm_expanded_size, + __ad["firm_num_actions"], + self.freeze_firms, + ) + firm_optim = NoOpOptimizer() + else: + firm_policy = PolicyNet( + firm_expanded_size, + __ad["firm_num_actions"], + norm_consts=( + torch.zeros(firm_expanded_size).cuda(), + firm_state_scaling_factors(self.cfg_dict), + ), + ).to("cuda") + + firm_optim = torch.optim.Adam( + firm_policy.parameters(), + lr=lr * self.agents_dict.get("firm_lr_multiple", 1.0), + ) + + # -------------------------------------------- + # Define Government policy + optimizers + # -------------------------------------------- + government_expanded_size = size_after_digit_expansion( + __ad["government_state_dim"], + __ad["government_digit_dims"], + __td["digit_representation_size"], + ) + + if self.freeze_govt is not None: + government_policy = DeterministicPolicy( + government_expanded_size, + __ad["government_num_actions"], + self.freeze_govt, + ) + government_optim = NoOpOptimizer() + else: + government_policy = PolicyNet( + government_expanded_size, + __ad["government_num_actions"], + norm_consts=( + torch.zeros(government_expanded_size).cuda(), + govt_state_scaling_factors(self.cfg_dict), + ), + ).to("cuda") + government_optim = torch.optim.Adam( + government_policy.parameters(), + lr=lr * self.agents_dict.get("government_lr_multiple", 1.0), + ) + + # -------------------------------------------- + # Logging + # -------------------------------------------- + # For looking up GPU tensors + # -------------------------------------------- + agent_type_arrays = { + "consumer": ( + self.consumer_states_batch_gpu_tensor, + self.consumer_actions_batch_gpu_tensor, + self.consumer_rewards_batch_gpu_tensor, + ), + "firm": ( + self.firm_states_batch, + self.firm_actions_batch, + self.firm_rewards_batch, + ), + "government": ( + self.government_states_batch, + self.government_actions_batch, + self.government_rewards_batch, + ), + } + + agent_action_arrays = { + "consumer": __ad["consumer_work_actions_array"], + "firm": __ad["firm_actions_array"], + "government": __ad["government_actions_array"], + } + + agent_aux_arrays = { + "consumer": (self.consumer_aux_batch_gpu_tensor), + "firm": (self.firm_aux_batch), + "government": None, + } + + # -------------------------------------------- + # Training policy XYZ starts at which step? + # -------------------------------------------- + firm_no_ponzi_coef = self.agents_dict.get("firm_noponzi_start", 0.0) + consumer_no_ponzi_coef = self.agents_dict.get("consumer_noponzi_start", 0.0) + lagr_num_steps = self.train_dict.get("lagr_num_steps", 1) + + firm_training_start = self.agents_dict.get("firm_training_start", 0) + consumer_training_start = self.agents_dict.get("consumer_training_start", 0) + government_training_start = self.agents_dict.get("government_training_start", 0) + + firm_action_start = self.agents_dict.get("firm_begin_anneal_action", 0) + government_action_start = self.agents_dict.get( + "government_begin_anneal_action", 0 + ) + + # -------------------------------------------- + # Training loop + # -------------------------------------------- + if self.train_dict.get("infinite_episodes", False): + epi_iterator = itertools.count(0, 1) + else: + epi_iterator = range(__td["num_episodes"]) + + final_epi = None + for epi in tqdm(epi_iterator): + + firm_actions_mask = firm_action_mask( + self.cfg_dict, + max(epi - firm_action_start, 0), + ) + government_actions_mask = government_action_mask( + self.cfg_dict, + max(epi - government_action_start, 0), + ) + theta_coef = compute_theta_coef(self.cfg_dict, epi) + + # Reset environment for all agents + self.cuda_reset_env( + CudaTensorHolder(self.consumer_states_gpu_tensor), + CudaTensorHolder(self.firm_states_gpu_tensor), + CudaTensorHolder(self.government_states_gpu_tensor), + self.consumer_states_checkpoint_gpu_pycuda, + self.firm_states_checkpoint_gpu_pycuda, + self.government_states_checkpoint_gpu_pycuda, + theta_coef, + block=block, + grid=grid, + ) + + # Learning Loop + for _iter in range(num_iters): + + # ------------------------ + # Run policy and get probs + # ------------------------ + with torch.no_grad(): + # here, we must perform digit scaling + consumer_probs_list, _ = consumer_policy( + expand_to_digit_form( + self.consumer_states_gpu_tensor, + __ad["consumer_digit_dims"], + __td["digit_representation_size"], + ) + ) + firm_probs, _ = firm_policy( + expand_to_digit_form( + self.firm_states_gpu_tensor, + __ad["firm_digit_dims"], + __td["digit_representation_size"], + ), + actions_mask=firm_actions_mask, + ) + government_probs, _ = government_policy( + expand_to_digit_form( + self.government_states_gpu_tensor, + __ad["government_digit_dims"], + __td["digit_representation_size"], + ), + actions_mask=government_actions_mask, + ) + + # ------------------------ + # Get action samples + # ------------------------ + # Sample consumer actions using PyTorch here on GPU! + self.sample_consumer_actions_and_store(consumer_probs_list) + + # Sample firms + govt actions using PyCUDA on GPU! + self.cuda_sample_actions( + CudaTensorHolder(firm_probs), + self.firm_action_indices_gpu_pycuda, + self.firm_actions_gpu_pycuda, + CudaTensorHolder(government_probs), + self.government_action_indices_gpu_pycuda, + self.government_actions_gpu_pycuda, + block=block, + grid=grid, + ) + + # ------------------------ + # Step on GPU + # ------------------------ + self.cuda_step( + CudaTensorHolder( + # size: batches x n_consumers x consumer_state float + self.consumer_states_gpu_tensor + ), + CudaTensorHolder( + # size: batches x n_consumers x consumer_action_dim float + self.consumer_actions_single_gpu_tensor + ), + # size: batches x n_consumers x 1 float + self.consumer_rewards_gpu_pycuda, + CudaTensorHolder( + # size: batches x episode x n_consumers x consumer_state float + self.consumer_states_batch_gpu_tensor + ), + CudaTensorHolder(self.consumer_rewards_batch_gpu_tensor), + CudaTensorHolder(self.firm_states_gpu_tensor), + self.firm_action_indices_gpu_pycuda, + self.firm_actions_gpu_pycuda, + self.firm_rewards_gpu_pycuda, + CudaTensorHolder(self.firm_states_batch), + CudaTensorHolder(self.firm_actions_batch), + CudaTensorHolder(self.firm_rewards_batch), + CudaTensorHolder(self.government_states_gpu_tensor), + self.government_action_indices_gpu_pycuda, + self.government_actions_gpu_pycuda, + self.government_rewards_gpu_pycuda, + CudaTensorHolder(self.government_states_batch), + CudaTensorHolder(self.government_actions_batch), + CudaTensorHolder(self.government_rewards_batch), + CudaTensorHolder(self.consumer_aux_batch_gpu_tensor), + CudaTensorHolder(self.firm_aux_batch), + np.int32(_iter), + block=block, + grid=grid, + ) + self.consumer_actions_batch_gpu_tensor[ + :, _iter, :, : + ] = self.consumer_actions_index_single_gpu_tensor + + # ------------------------ + # Add penalty for no-Ponzi + # ------------------------ + add_penalty_for_no_ponzi( + self.firm_states_gpu_tensor, + self.firm_rewards_batch, + __ad["global_state_dim"], + penalty_coef=firm_no_ponzi_coef, + ) + add_penalty_for_no_ponzi( + self.consumer_states_gpu_tensor, + self.consumer_rewards_batch_gpu_tensor, + __ad["global_state_dim"], + penalty_coef=consumer_no_ponzi_coef, + penalty_scale=__ad["consumer_penalty_scale"], + ) + + # add government rewards -- sum of consumer rewards + update_government_rewards( + self.government_rewards_batch, + self.consumer_rewards_batch_gpu_tensor, + self.firm_rewards_batch, + self.cfg_dict, + ) + + # Save dense logs + # ------------------------ + if (epi % __td["save_model_every"]) == 0: + save_policy_parameters( + self.save_dir, + epi, + consumer_policy, + firm_policy, + government_policy, + self.freeze_firms, + self.freeze_govt, + ) + if (epi % self.save_dense_every) == 0: + save_dense_log( + self.save_dir, + epi, + agent_type_arrays, + agent_action_arrays, + agent_aux_arrays, + ) + + # -------------------------------- + # Curriculum: Train Consumers + # -------------------------------- + if self.consumers_will_train_this_episode(epi): + consumer_entropy_coef = anneal_entropy_coef( + self.agents_dict.get("consumer_anneal_entropy", None), + epi - consumer_training_start, + ) + consumer_reward_scale = self.agents_dict.get( + "consumer_reward_scale", 1.0 + ) + if __td["use_ppo"]: + consumer_ppo_step( + consumer_policy, + expand_to_digit_form( + self.consumer_states_batch_gpu_tensor, + __ad["consumer_digit_dims"], + __td["digit_representation_size"], + ), + self.consumer_actions_batch_gpu_tensor, + self.consumer_rewards_batch_gpu_tensor, + consumer_optim, + __td["gamma"], + entropy_val=consumer_entropy_coef * __td["entropy"], + value_loss_weight=__td["value_loss_weight"], + reward_scale=consumer_reward_scale, + ppo_num_updates=__td["ppo_num_updates"], + clip_param=__td["ppo_clip_param"], + clip_grad_norm=self.train_dict.get("clip_grad_norm", None), + ) + else: + consumer_policy_gradient_step( + consumer_policy, + expand_to_digit_form( + self.consumer_states_batch_gpu_tensor, + __ad["consumer_digit_dims"], + __td["digit_representation_size"], + ), + self.consumer_actions_batch_gpu_tensor, + self.consumer_rewards_batch_gpu_tensor, + consumer_optim, + __td["gamma"], + entropy_val=consumer_entropy_coef * __td["entropy"], + value_loss_weight=__td["value_loss_weight"], + reward_scale=consumer_reward_scale, + clip_grad_norm=self.train_dict.get("clip_grad_norm", None), + ) + if (epi % lagr_num_steps) == 0: + consumer_no_ponzi_coef = update_penalty_coef( + self.consumer_states_gpu_tensor, + __ad["global_state_dim"], + consumer_no_ponzi_coef, + penalty_step_size=__ad["consumer_noponzi_eta"], + penalty_scale=__ad["consumer_penalty_scale"], + ) + else: + pass + + # -------------------------------- + # Curriculum: Train Firms + # -------------------------------- + if self.firms_will_train_this_episode(epi): + firm_entropy_coef = anneal_entropy_coef( + self.agents_dict.get("firm_anneal_entropy", None), + epi - firm_training_start, + ) + firm_reward_scale = self.agents_dict.get("firm_reward_scale", 1.0) + if __td["use_ppo"]: + ppo_step( + firm_policy, + expand_to_digit_form( + self.firm_states_batch, + __ad["firm_digit_dims"], + __td["digit_representation_size"], + ), + self.firm_actions_batch, + self.firm_rewards_batch, + firm_optim, + __td["gamma"], + entropy_val=firm_entropy_coef * __td["entropy"], + value_loss_weight=__td["value_loss_weight"], + actions_mask=firm_actions_mask, + reward_scale=firm_reward_scale, + ppo_num_updates=__td["ppo_num_updates"], + clip_param=__td["ppo_clip_param"], + clip_grad_norm=self.train_dict.get("clip_grad_norm", None), + ) + else: + policy_gradient_step( + firm_policy, + expand_to_digit_form( + self.firm_states_batch, + __ad["firm_digit_dims"], + __td["digit_representation_size"], + ), + self.firm_actions_batch, + self.firm_rewards_batch, + firm_optim, + __td["gamma"], + entropy_val=firm_entropy_coef * __td["entropy"], + value_loss_weight=__td["value_loss_weight"], + actions_mask=firm_actions_mask, + reward_scale=firm_reward_scale, + clip_grad_norm=self.train_dict.get("clip_grad_norm", None), + ) + + if (epi % lagr_num_steps) == 0: + firm_no_ponzi_coef = update_penalty_coef( + self.firm_states_gpu_tensor, + __ad["global_state_dim"], + firm_no_ponzi_coef, + penalty_step_size=__ad["firm_noponzi_eta"], + ) + else: + pass + + # -------------------------------- + # Curriculum: Train Governments + # -------------------------------- + if self.governments_will_train_this_episode(epi): + government_entropy_coef = anneal_entropy_coef( + self.agents_dict.get("govt_anneal_entropy", None), + epi - government_training_start, + ) + government_reward_scale = self.agents_dict.get( + "government_reward_scale", 1.0 + ) + if __td["use_ppo"]: + ppo_step( + government_policy, + expand_to_digit_form( + self.government_states_batch, + __ad["government_digit_dims"], + __td["digit_representation_size"], + ), + self.government_actions_batch, + self.government_rewards_batch, + government_optim, + __td["gamma"], + entropy_val=government_entropy_coef * __td["entropy"], + value_loss_weight=__td["value_loss_weight"], + actions_mask=government_actions_mask, + reward_scale=government_reward_scale, + ppo_num_updates=__td["ppo_num_updates"], + clip_param=__td["ppo_clip_param"], + clip_grad_norm=self.train_dict.get("clip_grad_norm", None), + ) + else: + policy_gradient_step( + government_policy, + expand_to_digit_form( + self.government_states_batch, + __ad["government_digit_dims"], + __td["digit_representation_size"], + ), + self.government_actions_batch, + self.government_rewards_batch, + government_optim, + __td["gamma"], + entropy_val=government_entropy_coef * __td["entropy"], + value_loss_weight=__td["value_loss_weight"], + actions_mask=government_actions_mask, + reward_scale=government_reward_scale, + clip_grad_norm=self.train_dict.get("clip_grad_norm", None), + ) + else: + pass + + # Store the value of the final episode + final_epi = epi + + # ------------------------------------------------------------------ + # Post-Training (may not reach this with an infinite training loop!) + # Save FINAL dense log. + # ------------------------------------------------------------------ + save_dense_log( + self.save_dir, + "final", + agent_type_arrays, + agent_action_arrays, + agent_aux_arrays, + ) + save_policy_parameters( + self.save_dir, + final_epi, + consumer_policy, + firm_policy, + government_policy, + self.freeze_firms, + self.freeze_govt, + ) + + # ------------------------------------------------------------------ + # Clean up + # ------------------------------------------------------------------ + + self.cuda_free_mem(block=block, grid=grid) + + +class CudaTensorHolder(pycuda.driver.PointerHolderBase): + """ + A class that facilitates casting tensors to pointers. + """ + + def __init__(self, t): + super().__init__() + self.t = t + self.gpudata = t.data_ptr() + + def get_pointer(self): + return self.t.data_ptr() diff --git a/ai_economist/real_business_cycle/rbc/networks.py b/ai_economist/real_business_cycle/rbc/networks.py new file mode 100644 index 0000000..245b187 --- /dev/null +++ b/ai_economist/real_business_cycle/rbc/networks.py @@ -0,0 +1,114 @@ +# Copyright (c) 2021, salesforce.com, inc. +# All rights reserved. +# SPDX-License-Identifier: BSD-3-Clause +# For full license text, see the LICENSE file in the repo root +# or https://opensource.org/licenses/BSD-3-Clause + +import torch +import torch.nn.functional as F +from torch import nn + + +class IndependentPolicyNet(nn.Module): + """ + Represents a policy network with separate heads for different types of actions. + Thus, the resulting policy will take the form + $pi(a | s) = pi_1(a_1 | s) pi_2(a_2 | s)...$ + """ + + def __init__(self, state_size, action_size_list, norm_consts=None): + super().__init__() + + self.state_size = state_size + self.action_size_list = action_size_list + if norm_consts is not None: + self.norm_center, self.norm_scale = norm_consts + else: + self.norm_center = torch.zeros(self.state_size).cuda() + self.norm_scale = torch.ones(self.state_size).cuda() + self.fc1 = nn.Linear(state_size, 128) + self.fc2 = nn.Linear(128, 128) + # policy network head + self.action_heads = nn.ModuleList( + [nn.Linear(128, action_size) for action_size in action_size_list] + ) + # value network head + self.fc4 = nn.Linear(128, 1) + + def forward(self, x): + assert x.shape[-1] == self.state_size # Check if the last dimension matches + + # Normalize the model input + new_shape = tuple(1 for _ in x.shape[:-1]) + (x.shape[-1],) + view_center = self.norm_center.view(new_shape) + view_scale = self.norm_scale.view(new_shape) + x = (x - view_center) / view_scale + + # Feed forward + x = F.relu(self.fc1(x)) + x = F.relu(self.fc2(x)) + probs = [F.softmax(action_head(x), dim=-1) for action_head in self.action_heads] + vals = self.fc4(x) + return probs, vals + + +class PolicyNet(nn.Module): + """ + The policy network class to output acton probabilities and the value function. + """ + + def __init__(self, state_size, action_size, norm_consts=None): + super().__init__() + + self.state_size = state_size + self.action_size = action_size + if norm_consts is not None: + self.norm_center, self.norm_scale = norm_consts + else: + self.norm_center = torch.zeros(self.state_size).cuda() + self.norm_scale = torch.ones(self.state_size).cuda() + self.fc1 = nn.Linear(state_size, 128) + self.fc2 = nn.Linear(128, 128) + # policy network head + self.fc3 = nn.Linear(128, action_size) + # value network head + self.fc4 = nn.Linear(128, 1) + + def forward(self, x, actions_mask=None): + # here, the action mask should be large negative constants for actions + # that shouldn't be allowed. + new_shape = tuple(1 for _ in x.shape[:-1]) + (x.shape[-1],) + view_center = self.norm_center.view(new_shape) + view_scale = self.norm_scale.view(new_shape) + x = (x - view_center) / view_scale + x = F.relu(self.fc1(x)) + x = F.relu(self.fc2(x)) + if actions_mask is not None: + probs = F.softmax(self.fc3(x) + actions_mask, dim=-1) + else: + probs = F.softmax(self.fc3(x), dim=-1) + vals = self.fc4(x) + return probs, vals + + +class DeterministicPolicy: + """ + A policy class that outputs deterministic actions. + """ + + def __init__(self, state_size, action_size, action_choice): + self.state_size = state_size + self.action_size = action_size + self.action_choice = action_choice + self.actions_out = torch.zeros(action_size, device="cuda") + self.actions_out[self.action_choice] = 1.0 + + def __call__(self, x, actions_mask=None): + return self.forward(x) + + def forward(self, x): + # output enough copies of the delta function + # distribution of the right size given x + x_batch_shapes = x.shape[:-1] + repeat_vals = x_batch_shapes + (1,) + return self.actions_out.repeat(*repeat_vals), None diff --git a/ai_economist/real_business_cycle/rbc/util.py b/ai_economist/real_business_cycle/rbc/util.py new file mode 100644 index 0000000..6e09a15 --- /dev/null +++ b/ai_economist/real_business_cycle/rbc/util.py @@ -0,0 +1,110 @@ +# Copyright (c) 2021, salesforce.com, inc. +# All rights reserved. +# SPDX-License-Identifier: BSD-3-Clause +# For full license text, see the LICENSE file in the repo root +# or https://opensource.org/licenses/BSD-3-Clause + +import torch + + +def dict_merge(dct, merge_dct): + """Recursive dict merge. Inspired by :meth:``dict.update()``, instead of + updating only top-level keys, dict_merge recurses down into dicts nested + to an arbitrary depth, updating keys. The ``merge_dct`` is merged into + ``dct``. + :param dct: dict onto which the merge is executed + :param merge_dct: dct merged into dct + :return: None + """ + for k, v in merge_dct.items(): + # dct does not have k yet. Add it with value v. + if (k not in dct) and (not isinstance(dct, list)): + dct[k] = v + else: + # dct[k] and merge_dict[k] are both dictionaries. Recurse. + if isinstance(dct[k], (dict, list)) and isinstance(v, dict): + dict_merge(dct[k], merge_dct[k]) + else: + # dct[k] and merge_dict[k] are both tuples or lists. + if isinstance(dct[k], (tuple, list)) and isinstance(v, (tuple, list)): + # They don't match. Overwrite with v. + if len(dct[k]) != len(v): + dct[k] = v + else: + for i, (d_val, v_val) in enumerate(zip(dct[k], v)): + if isinstance(d_val, dict) and isinstance(v_val, dict): + dict_merge(d_val, v_val) + else: + dct[k][i] = v_val + else: + dct[k] = v + + +def min_max_consumer_budget_delta(hparams_dict): + # largest single round changes + max_wage = hparams_dict["agents"]["max_possible_wage"] + max_hours = hparams_dict["agents"]["max_possible_hours_worked"] + max_price = hparams_dict["agents"]["max_possible_price"] + max_singlefirm_consumption = hparams_dict["agents"]["max_possible_consumption"] + num_firms = hparams_dict["agents"]["num_firms"] + + min_budget = ( + -max_singlefirm_consumption * max_price * num_firms + ) # negative budget from consuming only + max_budget = max_hours * max_wage * num_firms # positive budget from only working + return min_budget, max_budget + + +def min_max_stock_delta(hparams_dict): + # for now, assuming 1.0 capital + max_hours = hparams_dict["agents"]["max_possible_hours_worked"] + max_singlefirm_consumption = hparams_dict["agents"]["max_possible_consumption"] + alpha = hparams_dict["world"]["production_alpha"] + if isinstance(alpha, str): + alpha = 0.8 + num_consumers = hparams_dict["agents"]["num_consumers"] + max_delta = (max_hours * num_consumers) ** alpha + min_delta = -max_singlefirm_consumption * num_consumers + return min_delta, max_delta + + +def min_max_firm_budget(hparams_dict): + max_wage = hparams_dict["agents"]["max_possible_wage"] + max_hours = hparams_dict["agents"]["max_possible_hours_worked"] + max_singlefirm_consumption = hparams_dict["agents"]["max_possible_consumption"] + num_consumers = hparams_dict["agents"]["num_consumers"] + max_price = hparams_dict["agents"]["max_possible_price"] + max_delta = max_singlefirm_consumption * max_price * num_consumers + min_delta = -max_hours * max_wage * num_consumers + return min_delta, max_delta + + +def expand_to_digit_form(x, dims_to_expand, max_digits): + # first split x up + requires_grad = ( + x.requires_grad + ) # don't want to backprop through these ops, but do want + # gradients if x had them + with torch.no_grad(): + tensor_pieces = [] + expanded_digit_shape = x.shape[:-1] + (max_digits,) + for i in range(x.shape[-1]): + if i not in dims_to_expand: + tensor_pieces.append(x[..., i : i + 1]) + else: + digit_entries = torch.zeros(expanded_digit_shape, device=x.device) + for j in range(max_digits): + digit_entries[..., j] = (x[..., i] % (10 ** (j + 1))) / ( + 10 ** (j + 1) + ) + tensor_pieces.append(digit_entries) + + output = torch.cat(tensor_pieces, dim=-1) + output.requires_grad_(requires_grad) + return output + + +def size_after_digit_expansion(existing_size, dims_to_expand, max_digits): + num_expanded = len(dims_to_expand) + # num non expanded digits, + all the expanded ones + return (existing_size - num_expanded) + (max_digits * num_expanded) diff --git a/ai_economist/real_business_cycle/train_bestresponse.py b/ai_economist/real_business_cycle/train_bestresponse.py new file mode 100644 index 0000000..c1731d4 --- /dev/null +++ b/ai_economist/real_business_cycle/train_bestresponse.py @@ -0,0 +1,108 @@ +# Copyright (c) 2021, salesforce.com, inc. +# All rights reserved. +# SPDX-License-Identifier: BSD-3-Clause +# For full license text, see the LICENSE file in the repo root +# or https://opensource.org/licenses/BSD-3-Clause + +import argparse +import pickle +from collections import defaultdict +from pathlib import Path + +import numpy as np +from experiment_utils import cfg_dict_from_yaml +from rbc.cuda_manager import ConsumerFirmRunManagerBatchParallel + + +def check_if_ep_str_policy_exists(rollout_path, ep_str): + return ( + rollout_path / Path("saved_models") / Path(f"consumer_policy_{ep_str}.pt") + ).is_file() + + +def run_rollout(rollout_path, arguments): + """ + # take in rollout directory + # load latest policies and the action functions and the hparams dict + # make a cudamanager obj and run the job + # this will require initializing everything as before, resetting, + # and running naive policy gradient training at some fixed learning rate + """ + with open(rollout_path / Path("action_arrays.pickle"), "rb") as f: + action_arrays = pickle.load(f) + + consumption_choices, work_choices, price_and_wage, tax_choices = ( + action_arrays["consumption_choices"], + action_arrays["work_choices"], + action_arrays["price_and_wage"], + action_arrays["tax_choices"], + ) + + cfg_dict = cfg_dict_from_yaml( + rollout_path / Path("hparams.yaml"), + consumption_choices, + work_choices, + price_and_wage, + tax_choices, + ) + + print(cfg_dict) + + if arguments.agent_type == "all": + agent_types = ["consumer", "firm", "government"] + else: + agent_types = [arguments.agent_type] + + for agent_type in agent_types: + ep_rewards = defaultdict(list) + for _ in range(arguments.repeat_runs): + for ep_str in arguments.ep_strs: + if not check_if_ep_str_policy_exists(rollout_path, ep_str): + print(f"warning: {rollout_path} {ep_str} policy not found") + ep_rewards[ep_str].append([0.0]) + continue + m = ConsumerFirmRunManagerBatchParallel(cfg_dict) + rewards_start = m.bestresponse_train( + agent_type, + arguments.num_episodes, + rollout_path, + ep_str=ep_str, + checkpoint=arguments.checkpoint_model, + ) + ep_rewards[ep_str].append(rewards_start) + + with open(rollout_path / Path(f"br_{agent_type}_output.txt"), "w") as f: + for ep_str in arguments.ep_strs: + reward_arr = np.array(ep_rewards[ep_str]) + print( + f"mean reward (std) on rollout {ep_str}: " + f"before BR training {reward_arr[:,0].mean()} " + f"({reward_arr[:,0].std()}), " + f"after BR training {reward_arr[:,-1].mean()} " + f"({reward_arr[:,-1].std()}), " + f"mean improvement {(reward_arr[:,-1]-reward_arr[:,0]).mean()} " + f"({(reward_arr[:,-1]-reward_arr[:,0]).std()}", + file=f, + ) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("rolloutdir", type=str) + parser.add_argument("num_episodes", type=int) + parser.add_argument("--experiment-dir", action="store_true") + parser.add_argument("--ep-strs", nargs="+", default=["0", "latest"]) + parser.add_argument("--agent-type", type=str, default="all") + parser.add_argument("--repeat-runs", type=int, default=1) + parser.add_argument("--checkpoint-model", type=int, default=100) + + args = parser.parse_args() + + if args.experiment_dir: + exp_dir = Path(args.rolloutdir) + for rolloutpath in exp_dir.iterdir(): + if rolloutpath.is_dir(): + run_rollout(rolloutpath, args) + else: + rolloutpath = Path(args.rolloutdir) + run_rollout(rolloutpath, args) diff --git a/ai_economist/real_business_cycle/train_multi_exps.py b/ai_economist/real_business_cycle/train_multi_exps.py new file mode 100644 index 0000000..df0c428 --- /dev/null +++ b/ai_economist/real_business_cycle/train_multi_exps.py @@ -0,0 +1,119 @@ +# Copyright (c) 2021, salesforce.com, inc. +# All rights reserved. +# SPDX-License-Identifier: BSD-3-Clause +# For full license text, see the LICENSE file in the repo root +# or https://opensource.org/licenses/BSD-3-Clause + +import argparse +import os + +from experiment_utils import ( + create_job_dir, + run_experiment_batch_parallel, + sweep_cfg_generator, +) +from rbc.constants import all_agents_short_export_experiment_template + +train_param_sweeps = { + "lr": [0.001], + "entropy": [0.5], + "batch_size": [128], + "clip_grad_norm": [2.0], + "base_seed": [2345], + "should_boost_firm_reward": [False], + "use_ppo": [True], + "ppo_num_updates": [2, 4], + "ppo_clip_param": [0.1], +} + + +agent_param_sweeps = { + "consumer_lr_multiple": [1.0], + "consumer_reward_scale": [5.0], + "government_reward_scale": [5.0 * 100.0 * 2.0], + "firm_reward_scale": [30000], + "government_counts_firm_reward": [1], + "government_lr_multiple": [0.05], +} + + +world_param_sweeps = { + "initial_wages": [0.0], + "interest_rate": [0.0], + "importer_price": [500.0], + "importer_quantity": [100.0], + "use_importer": [1], +} + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--dry-run", action="store_true") + parser.add_argument("--experiment-dir", type=str, default="experiment/experiment") + parser.add_argument("--group-name", type=str, default="default_group") + parser.add_argument("--job-name-base", type=str, default="rollout") + parser.add_argument("--num-consumers", type=int, default=100) + parser.add_argument("--num-firms", type=int, default=10) + parser.add_argument("--num-governments", type=int, default=1) + parser.add_argument("--run-only", action="store_true") + parser.add_argument("--seed-from-timestamp", action="store_true") + + args = parser.parse_args() + + ( + default_cfg_dict, + consumption_choices, + work_choices, + price_and_wage, + tax_choices, + default_firm_action, + default_government_action, + ) = all_agents_short_export_experiment_template( + args.num_firms, args.num_consumers, args.num_governments + ) + + if args.run_only: + print("Not sweeping over hyperparameter combos...") + else: + for new_cfg in sweep_cfg_generator( + default_cfg_dict, + tr_param_sweeps=train_param_sweeps, + ag_param_sweeps=agent_param_sweeps, + wld_param_sweeps=world_param_sweeps, + seed_from_timestamp=args.seed_from_timestamp, + group_name=args.group_name, + ): + create_job_dir( + args.experiment_dir, + args.job_name_base, + cfg=new_cfg, + action_arrays={ + "consumption_choices": consumption_choices, + "work_choices": work_choices, + "price_and_wage": price_and_wage, + "tax_choices": tax_choices, + }, + ) + + if args.dry_run: + print("Dry-run -> not actually training...") + else: + print("Training multiple experiments locally...") + + # for dirs in experiment dir, run job + experiment_dirs = [ + f.path for f in os.scandir(args.experiment_dir) if f.is_dir() + ] + for experiment in experiment_dirs: + run_experiment_batch_parallel( + experiment, + consumption_choices, + work_choices, + price_and_wage, + tax_choices, + group_name=args.group_name, + consumers_only=False, + no_firms=False, + default_firm_action=default_firm_action, + default_government_action=default_government_action, + ) diff --git a/ai_economist/real_business_cycle/train_single_exp.py b/ai_economist/real_business_cycle/train_single_exp.py new file mode 100644 index 0000000..ca15c65 --- /dev/null +++ b/ai_economist/real_business_cycle/train_single_exp.py @@ -0,0 +1,51 @@ +# Copyright (c) 2021, salesforce.com, inc. +# All rights reserved. +# SPDX-License-Identifier: BSD-3-Clause +# For full license text, see the LICENSE file in the repo root +# or https://opensource.org/licenses/BSD-3-Clause + +import argparse + +from experiment_utils import run_experiment_batch_parallel +from rbc.constants import all_agents_export_experiment_template + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--dry-run", action="store_true") + parser.add_argument("--experiment-dir", type=str, default="experiment/experiment") + parser.add_argument("--group-name", type=str, default="default_group") + parser.add_argument("--job-name-base", type=str, default="rollout") + parser.add_argument("--num-consumers", type=int, default=100) + parser.add_argument("--num-firms", type=int, default=10) + parser.add_argument("--num-governments", type=int, default=1) + parser.add_argument("--run-only", action="store_true") + parser.add_argument("--seed-from-timestamp", action="store_true") + + args = parser.parse_args() + ( + default_cfg_dict, + consumption_choices, + work_choices, + price_and_wage, + tax_choices, + default_firm_action, + default_government_action, + ) = all_agents_export_experiment_template( + args.num_firms, args.num_consumers, args.num_governments + ) + + if not args.dry_run: + # for dirs in experiment dir, run job + experiment = args.experiment_dir + run_experiment_batch_parallel( + experiment, + consumption_choices, + work_choices, + price_and_wage, + tax_choices, + group_name=args.group_name, + consumers_only=False, + no_firms=False, + default_firm_action=default_firm_action, + default_government_action=default_government_action, + ) diff --git a/ai_economist/training/__init__.py b/ai_economist/training/__init__.py new file mode 100644 index 0000000..93bee4b --- /dev/null +++ b/ai_economist/training/__init__.py @@ -0,0 +1,5 @@ +# Copyright (c) 2021, salesforce.com, inc. +# All rights reserved. +# SPDX-License-Identifier: BSD-3-Clause +# For full license text, see the LICENSE file in the repo root +# or https://opensource.org/licenses/BSD-3-Clause diff --git a/ai_economist/training/run_configs/covid_and_economy_environment.yaml b/ai_economist/training/run_configs/covid_and_economy_environment.yaml new file mode 100644 index 0000000..4bc1d21 --- /dev/null +++ b/ai_economist/training/run_configs/covid_and_economy_environment.yaml @@ -0,0 +1,77 @@ +# Copyright (c) 2021, salesforce.com, inc. +# All rights reserved. +# SPDX-License-Identifier: BSD-3-Clause +# For full license text, see the LICENSE file in the repo root +# or https://opensource.org/licenses/BSD-3-Clause + +# YAML configuration for the tag continuous environment +name: "covid_and_economy_environment" +# Environment settings +env: + collate_agent_step_and_reset_data: True + components: + - ControlUSStateOpenCloseStatus: + action_cooldown_period: 28 + - FederalGovernmentSubsidy: + num_subsidy_levels: 20 + subsidy_interval: 90 + max_annual_subsidy_per_person: 20000 + - VaccinationCampaign: + daily_vaccines_per_million_people: 3000 + delivery_interval: 1 + vaccine_delivery_start_date: "2021-01-12" + economic_reward_crra_eta: 2 + episode_length: 540 + flatten_masks: True + flatten_observations: False + health_priority_scaling_agents: 0.3 + health_priority_scaling_planner: 0.45 + infection_too_sick_to_work_rate: 0.1 + multi_action_mode_agents: False + multi_action_mode_planner: False + n_agents: 51 + path_to_data_and_fitted_params: "" + pop_between_age_18_65: 0.6 + risk_free_interest_rate: 0.03 + world_size: [1, 1] + start_date: "2020-03-22" + use_real_world_data: False + use_real_world_policies: False +# Trainer settings +trainer: + num_envs: 60 # number of environment replicas + num_episodes: 1000 # number of episodes to run the training for + train_batch_size: 5400 # total batch size used for training per iteration (across all the environments) +# Policy network settings +policy: # list all the policies below + a: + to_train: True # flag indicating whether the model needs to be trained + algorithm: "PPO" # algorithm used to train the policy + vf_loss_coeff: 1 # loss coefficient schedule for the value function loss + entropy_coeff: 0.05 # loss coefficient schedule for the entropy loss + gamma: 0.98 # discount factor + lr: 0.0001 # learning rate + model: + type: "fully_connected" + fc_dims: [256, 256] + model_ckpt_filepath: "" + p: + to_train: True + algorithm: "PPO" + vf_loss_coeff: 1 + entropy_coeff: # annealing entropy over time + - [0, 0.5] + - [50000000, 0.05] + gamma: 0.98 + lr: 0.0001 + model: + type: "fully_connected" + fc_dims: [256, 256] + model_ckpt_filepath: "" +# Checkpoint saving setting +saving: + metrics_log_freq: 100 # How often (in iterations) to print the metrics + model_params_save_freq: 500 # How often (in iterations) to save the model parameters + basedir: "/tmp" # base folder used for saving + name: "covid19_and_economy" # experiment name + tag: "experiments" # experiment tag diff --git a/ai_economist/training/training_script.py b/ai_economist/training/training_script.py new file mode 100644 index 0000000..7a13e80 --- /dev/null +++ b/ai_economist/training/training_script.py @@ -0,0 +1,134 @@ +# Copyright (c) 2021, salesforce.com, inc. +# All rights reserved. +# SPDX-License-Identifier: BSD-3-Clause +# For full license text, see the LICENSE file in the repo root +# or https://opensource.org/licenses/BSD-3-Clause + +""" +Example training script for the grid world and continuous versions of Tag. +Note: This training script only runs on a GPU machine. +You will also need to install WarpDrive (https://github.com/salesforce/warp-drive) +using `pip install rl-warp-drive`, and Pytorch(https://pytorch.org/) +""" + +import argparse +import logging +import os + +import GPUtil + +try: + num_gpus_available = len(GPUtil.getAvailable()) + assert num_gpus_available > 0, "This training script needs a GPU to run!" + print(f"Inside training_script.py: {num_gpus_available} GPUs are available.") + import torch + import yaml + from warp_drive.training.trainer import Trainer + from warp_drive.utils.env_registrar import EnvironmentRegistrar +except ModuleNotFoundError: + raise ModuleNotFoundError( + "This training script requires the 'WarpDrive' package, please run " + "'pip install rl-warp-drive' first." + ) from None +except ValueError: + raise ValueError("This training script needs a GPU to run!") from None + +from ai_economist.foundation.env_wrapper import FoundationEnvWrapper +from ai_economist.foundation.scenarios.covid19.covid19_env import ( + CovidAndEconomyEnvironment, +) + +logging.getLogger().setLevel(logging.ERROR) + +pytorch_cuda_init_success = torch.cuda.FloatTensor(8) +_COVID_AND_ECONOMY_ENVIRONMENT = "covid_and_economy_environment" + +# Usage: +# >> python ai_economist/training/example_training_script.py +# --env covid_and_economy_environment + + +if __name__ == "__main__": + + parser = argparse.ArgumentParser() + parser.add_argument("--env", "-e", type=str, help="Environment to train.") + + args = parser.parse_args() + + # Read the run configurations specific to each environment. + # Note: The run config yamls are located at warp_drive/training/run_configs + # --------------------------------------------------------------------------- + assert args.env in [_COVID_AND_ECONOMY_ENVIRONMENT], ( + f"Currently, the only environment supported " + f"is {_COVID_AND_ECONOMY_ENVIRONMENT}" + ) + + config_path = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + "run_configs", + f"{args.env}.yaml", + ) + with open(config_path, "r", encoding="utf8") as f: + run_config = yaml.safe_load(f) + + num_envs = run_config["trainer"]["num_envs"] + + # Create a wrapped environment object via the EnvWrapper + # Ensure that use_cuda is set to True (in order to run on the GPU) + # ---------------------------------------------------------------- + if run_config["name"] == _COVID_AND_ECONOMY_ENVIRONMENT: + env_registrar = EnvironmentRegistrar() + this_file_dir = os.path.dirname(os.path.abspath(__file__)) + env_registrar.add_cuda_env_src_path( + CovidAndEconomyEnvironment.name, + os.path.join( + this_file_dir, "../foundation/scenarios/covid19/covid19_build.cu" + ), + ) + env_wrapper = FoundationEnvWrapper( + CovidAndEconomyEnvironment(**run_config["env"]), + num_envs=num_envs, + use_cuda=True, + env_registrar=env_registrar, + ) + else: + raise NotImplementedError + + # The policy_tag_to_agent_id_map dictionary maps + # policy model names to agent ids. + # ---------------------------------------------------- + policy_tag_to_agent_id_map = { + "a": [str(agent_id) for agent_id in range(env_wrapper.env.n_agents)], + "p": ["p"], + } + + # Flag indicating whether separate obs, actions and rewards placeholders + # have to be created for each policy. + # Set "create_separate_placeholders_for_each_policy" to True here + # since the agents and planner have different observation + # and action spaces. + separate_placeholder_per_policy = True + + # Flag indicating the observation dimension corresponding to + # 'num_agents'. + # Note: WarpDrive assumes that all the observation are shaped + # (num_agents, *feature_dim), i.e., the observation dimension + # corresponding to 'num_agents' is the first one. Instead, if the + # observation dimension corresponding to num_agents is the last one, + # we will need to permute the axes to align with WarpDrive's assumption + obs_dim_corresponding_to_num_agents = "last" + + # Trainer object + # -------------- + trainer = Trainer( + env_wrapper=env_wrapper, + config=run_config, + policy_tag_to_agent_id_map=policy_tag_to_agent_id_map, + create_separate_placeholders_for_each_policy=separate_placeholder_per_policy, + obs_dim_corresponding_to_num_agents=obs_dim_corresponding_to_num_agents, + ) + + # Perform training + # ---------------- + trainer.train() + trainer.graceful_close() diff --git a/envs/__init__.py b/envs/__init__.py new file mode 100644 index 0000000..3f503fb --- /dev/null +++ b/envs/__init__.py @@ -0,0 +1,5 @@ +from . import ( + simple_market, + econ_wrapper, + base_econ_wrapper + ) diff --git a/envs/base_econ_wrapper.py b/envs/base_econ_wrapper.py new file mode 100644 index 0000000..9731463 --- /dev/null +++ b/envs/base_econ_wrapper.py @@ -0,0 +1,169 @@ +from ai_economist.foundation.base import base_env +from threading import Event, Lock, Thread +from queue import Queue +class BaseEconVecEnv(): + """Base class for connecting reciever wrapper to a multi threaded econ simulation and training session""" + + base_notification=Event() #Notification for Base + reset_notification=Event() #Notification for recievers + step_notification=Event() #Notification for recievers + + action_edit_lock=Lock() + actor_actions={} + + stop_edit_lock=Lock() + stop=False + + vote_lock=Lock() + n_voters=0 + n_votes_reset=0 + + + + # States of Env + env_data_lock=Lock() + obs=None + rew=None + done=None + info=None + n_data_retrieved=0 + + def __init__(self, econ: base_env.BaseEnvironment): + self.env=econ + + def register_vote(self): + """Register reciever on base. Base now knows""" + + def run(self): + """Start the base wrapper""" + thr=Thread(target=self._run,daemon=True) + thr.run() + return thr + + def _run(self): + #Reset for run + self.base_notification.clear() + self.reset_notification.clear() + self.step_notification.clear() + + self.stop_edit_lock.release() + self.stop=False + self.action_edit_lock.release() + self.actor_actions={} + self.vote_lock.release() + self.reset_notification.clear() + self.n_votes_reset=0 + self.n_votes_step=0 + + self.env_data_lock.release() + self.obs=None + self.rew=None + self.done=None + self.info=None + #Reseting Env + self._reset() + + while True: + # Wait for notification + self.base_notification.wait() + self.base_notification.clear() # Cleard + #Check for stop signal + self.stop_edit_lock.acquire() + if self.stop: + return + self.stop_edit_lock.release() + + #check for reset + self.vote_lock.acquire() # we might edit votes + if self.n_voters==self.n_votes_reset: + ## perform reset + self.n_votes_reset=0 + self._reset() + self.vote_lock.release() + + #check for actions + self.action_edit_lock.acquire() + if self.env.n_agents==len(self.actor_actions.keys) & self.step_notification.is_set()==False: + # we have all the actions -> STEP + self._step() + self.action_edit_lock.release() # release actions + # we are done + + def stop_env(self): + """Stops the wrapper""" + self.stop_edit_lock.acquire() + self.stop=True + self.stop_edit_lock.release() + self.base_notification.set() + + + def _reset(self): + # Aquire Lock + self.env_data_lock.acquire() + self.n_votes_reset=0 + self.obs=self.env.reset() #Reset env + self.rew=None + self.done=None + self.info=None + self.env_data_lock.release() #Release lock + # Notify for reset + self.reset_notification.set() + + def _step(self): + """Steping interaly""" + + self.env_data_lock.acquire() + self.reset_notification.clear() # reset after first step + self.obs,self.rew,self.done,self.info=self.env.step(self.actor_actions) # write data + self.n_data_retrieved=0 + self.env_data_lock.release() + self.action_edit_lock.acquire() # prevent steps until everybody had the chanse to look at it + self.step_notification.set() # notify recievers + + def _prepare_step(self): + #prepare base for next step + self.action_edit_lock.acquire() # we are editing action data + if self.step_notification.is_set(): + self.step_notification.clear() + self.actor_actions={} + self.action_edit_lock.release() + + def reciever_request_step(self, actions): + """Submits actions to base processing queue. Actions as dict pairing of idx and action id""" + self._prepare_step() # New actions are bening submitted. Prepare base for new step + self.action_edit_lock.acquire() # Start to submit action dict + for k,v in actions: + if self.actor_actions[k]!=None: + raise Exception("Actor action has already been submitted. {}".format(k)) + self.actor_actions[k]=v + self.base_notification.set() #Alert base for action changes + self.action_edit_lock.release() + + def reciever_block_step(self): + """Returns with newest data after step request has been called. Blocks until all actors have submitted an action""" + self.step_notification.wait() # new data available + self.env_data_lock.acquire() # get data + obs=self.obs + rew=self.rew + done=self.done + info=self.info + self.n_data_retrieved+=1 + if self.n_data_retrieved>=self.n_voters: + self.action_edit_lock.release() # release the step so that new actions can be submitted + + self.env_data_lock.release() + return obs,rew,done,info + + def reciever_request_reset(self): + """Adds to vote count to reset. If limit is reached reset will occure""" + self.vote_lock.acquire() + self.n_votes_reset+=1 + self.vote_lock.release() + + def reciever_block_reset(self): + """Called after request will block until reset occures. Returns observations.""" + self.reset_notification.wait() + self.env_data_lock.acquire() + obs=self.obs + self.env_data_lock.release() + return obs diff --git a/envs/reciever_econ_wrapper.py b/envs/reciever_econ_wrapper.py new file mode 100644 index 0000000..7a17656 --- /dev/null +++ b/envs/reciever_econ_wrapper.py @@ -0,0 +1,67 @@ +from collections import OrderedDict +from copy import deepcopy +from typing import Any, Callable, List, Optional, Sequence, Type, Union +from ai_economist.foundation.base import base_env + +import gym +import gym.spaces +import numpy as np +from base_econ_wrapper import BaseEconVecEnv + +from stable_baselines3.common.vec_env.base_vec_env import VecEnv, VecEnvIndices, VecEnvObs, VecEnvStepReturn +from stable_baselines3.common.vec_env.util import copy_obs_dict, dict_to_obs, obs_space_info + +from ai_economist import foundation + +class RecieverEconVecEnv(gym.Env): + """Reciever part of BaseEconVecEnv. Filters by agent class and presents gym api to RL algos. Enables multi threading learning for different agent types.""" + def __init__(self, base_econ: BaseEconVecEnv, agent_classname: str): + self.base_econ=base_econ + base_econ.register_vote() + self.econ=base_econ.env + self.agent_name=agent_classname + self.agnet_idx=list(self.econ.world._agent_class_idx_map[agent_classname]) + self.idx_to_index={} + #create idx to index map + for i in range(len(self.agnet_idx)): + self.idx_to_index[self.agnet_idx[i]]=i + first_idx=self.agnet_idx[0] + + + def step_async(self, actions: dict) -> None: + """Submittes actions to Env. actions is a dict with idx -> action pair""" + data=self._dict_index_to_idx(actions) + self.base_econ.reciever_request_step(data) + + def _dict_idx_to_index(self, data): + data_out={} + for k,v in data.items(): + data_out[self.idx_to_index[k]]=v + return data_out + + def _dict_index_to_idx(self, data): + data_out={} + for k,v in data.items(): + data_out[self.agnet_idx[k]]=v + return data_out + + def step_wait(self): + #convert to econ actions + obs,rew,done,info=self.base_econ.reciever_block_step() + c_obs=self._dict_idx_to_index(obs) + c_rew=self._dict_idx_to_index(rew) + c_done=self._dict_idx_to_index(done) + c_info=self._dict_idx_to_index(info) + return c_obs,c_rew,c_done,c_info + + + def reset(self): + # env=foundation.make_env_instance(**self.config) + # self.env = env + self.base_econ.reciever_request_reset() + obs =self.base_econ.reciever_block_reset() + c_obs=self._dict_idx_to_index(obs) + return c_obs + + + diff --git a/reqirements.txt b/reqirements.txt index a671b83..765bbd9 100644 --- a/reqirements.txt +++ b/reqirements.txt @@ -1,3 +1,3 @@ -ai-economist + gym ray[rllib] \ No newline at end of file

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