diff --git a/.github/workflows/chatops-binder.yaml b/.github/workflows/chatops-binder.yaml deleted file mode 100644 index e8ea3d482..000000000 --- a/.github/workflows/chatops-binder.yaml +++ /dev/null @@ -1,34 +0,0 @@ -#./github/workflows/chatops-binder.yaml -name: Chatops Binder -on: [issue_comment] # issues and PRs are equivalent in terms of comments for the GitHub API - -jobs: - trigger-chatops: - # Make sure the comment is on a PR, and contains the command "/binder" - if: (github.event.issue.pull_request != null) && contains(github.event.comment.body, '/binder') - runs-on: ubuntu-latest - steps: - # Use the GitHub API to: - # (1) Get the branch name of the PR that has been commented on with "/binder" - # (2) make a comment on the PR with the binder badge - - name: comment on PR with Binder link - uses: actions/github-script@v6 - with: - github-token: ${{secrets.GITHUB_TOKEN}} - script: | - // Get the branch name - github.pulls.get({ - owner: context.repo.owner, - repo: context.repo.repo, - pull_number: context.payload.issue.number - }).then( (pr) => { - - // use the branch name to make a comment on the PR with a Binder badge - var BRANCH_NAME = pr.data.head.ref - github.issues.createComment({ - issue_number: context.payload.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - body: `[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/${context.repo.owner}/${context.repo.repo}/${BRANCH_NAME}) :point_left: Launch a binder notebook on this branch` - }) - }) diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index fc9332869..91af9ca34 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -26,73 +26,73 @@ jobs: contents: write steps: - - uses: actions/checkout@v3 - - - name: Set up Python 3.10 - uses: actions/setup-python@v4 - with: - python-version: "3.10" # Interpolation.py doesn't support Python 3.11 [2023-07] - cache: 'pip' - cache-dependency-path: | - requirements/base.txt - requirements/doc.txt - - - name: Install Pandoc - run: sudo apt-get install --yes pandoc - - - name: Update pip - run: python -m pip install --upgrade pip - - - name: Install HARK - run: python -m pip install .[doc] - - - name: Run Sphinx - run: > - sphinx-build - -M html Documentation HARK-docs - -T - -W - -j auto - - - name: Set up git for deployment - run: | - git config user.name "${{ github.actor }}" - git config user.email "${{ github.actor }}@users.noreply.github.com" - git config --local --unset-all http.https://github.com/.extraheader - - - name: Commit all rendered HTML files - run: | - git switch --orphan gh-pages - git add --all HARK-docs/html - git commit -qm "Documentation from @ ${{ github.repository }}@${{ github.sha }}" - - - name: Deploy to GitHub Pages - # Only deploy to Pages on pushes to HEAD - if: (github.repository_owner == 'Econ-ARK') && (github.event_name == 'push') && (github.ref_name == 'master') - run: > - git push - --force - https://x-access-token:${{ github.token }}@github.com/${{ github.repository }} - `git subtree split --prefix HARK-docs/html gh-pages`:refs/heads/gh-pages + - uses: actions/checkout@v3 + + - name: Set up Python 3.10 + uses: actions/setup-python@v4 + with: + python-version: "3.10" # Interpolation.py doesn't support Python 3.11 [2023-07] + cache: "pip" + cache-dependency-path: | + requirements/base.txt + requirements/doc.txt + + - name: Install Pandoc + run: sudo apt-get install --yes pandoc + + - name: Update pip + run: python -m pip install --upgrade pip + + - name: Install HARK + run: python -m pip install .[doc] + + - name: Run Sphinx + run: > + sphinx-build + -M html Documentation HARK-docs + -T + -W + -j auto + + - name: Set up git for deployment + run: | + git config user.name "${{ github.actor }}" + git config user.email "${{ github.actor }}@users.noreply.github.com" + git config --local --unset-all http.https://github.com/.extraheader + + - name: Commit all rendered HTML files + run: | + git switch --orphan gh-pages + git add --all HARK-docs/html + git commit -qm "Documentation from @ ${{ github.repository }}@${{ github.sha }}" + + - name: Deploy to GitHub Pages + # Only deploy to Pages on pushes to HEAD + if: (github.repository_owner == 'Econ-ARK') && (github.event_name == 'push') && (github.ref_name == 'master') + run: > + git push + --force + https://x-access-token:${{ github.token }}@github.com/${{ github.repository }} + `git subtree split --prefix HARK-docs/html gh-pages`:refs/heads/gh-pages lint: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - name: Set up Python - uses: actions/setup-python@v4 - with: - python-version: 3 - - name: Install dependencies - run: | - python -m pip install --upgrade pip - python -m pip install --upgrade sphinx-lint - - name: Lint documentation with sphinx-lint - run: > - sphinx-lint - --ignore Documentation/example_notebooks/GenIncProcessModel.py - --enable all - --max-line-length 85 - README.md - Documentation/ + - uses: actions/checkout@v3 + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: 3 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install --upgrade sphinx-lint + - name: Lint documentation with sphinx-lint + run: > + sphinx-lint + --ignore Documentation/example_notebooks/GenIncProcessModel.py + --enable all + --max-line-length 85 + README.md + Documentation/ diff --git a/.github/workflows/execute-notebooks.yml b/.github/workflows/execute-notebooks.yml index fdb5a8c6a..06cb735e7 100644 --- a/.github/workflows/execute-notebooks.yml +++ b/.github/workflows/execute-notebooks.yml @@ -5,7 +5,7 @@ on: workflow_dispatch: # 6.49 am (GMT) every Monday; time chosen at random schedule: - - cron: "49 6 * * MON" + - cron: "49 6 * * MON" # Limit workflow permissions permissions: @@ -29,47 +29,47 @@ jobs: pull-requests: write steps: - - uses: actions/checkout@v3 - - name: Set up Python 3.10 - uses: actions/setup-python@v4 - with: - python-version: "3.10" # Numba doesn't support Python 3.11 [2023-05] - cache: 'pip' - cache-dependency-path: | - requirements/base.txt - .github/workflows/execute-notebooks.yml + - uses: actions/checkout@v3 + - name: Set up Python 3.10 + uses: actions/setup-python@v4 + with: + python-version: "3.10" # Numba doesn't support Python 3.11 [2023-05] + cache: "pip" + cache-dependency-path: | + requirements/base.txt + .github/workflows/execute-notebooks.yml - - name: Install dependencies - run: | - python -m pip install --upgrade pip - python -m pip install . - # For LabeledModels.ipynb - python -m pip install estimagic - # For nbstripout - python -m pip install nbstripout - # For nb_exec.py - python -m pip install ipykernel nbclient nbformat + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install . + # For LabeledModels.ipynb + python -m pip install estimagic + # For nbstripout + python -m pip install nbstripout + # For nb_exec.py + python -m pip install ipykernel nbclient nbformat - - name: Strip output - run: nbstripout examples/**/*.ipynb + - name: Strip output + run: nbstripout examples/**/*.ipynb - # This step takes c. 20 minutes - - name: Execute notebooks - run: python tools/nb_exec.py examples/**/*.ipynb - env: - PYTHONUNBUFFERED: "1" + # This step takes c. 20 minutes + - name: Execute notebooks + run: python tools/nb_exec.py examples/**/*.ipynb + env: + PYTHONUNBUFFERED: "1" - - name: Open PR - uses: peter-evans/create-pull-request@v5 - with: - author: "Econ-ARK Bot " - branch: "bot/update-notebooks" - commit-message: "[bot] updated notebooks" - delete-branch: true - title: "[bot] Execute example notebooks" - # language=Markdown - body: > - This PR was [automatically generated] to re-execute - the example notebooks for use in the documentation. - - [automatically generated]: https://github.com/Econ-ARK/HARK/actions/workflows/execute-notebooks.yml + - name: Open PR + uses: peter-evans/create-pull-request@v5 + with: + author: "Econ-ARK Bot " + branch: "bot/update-notebooks" + commit-message: "[bot] updated notebooks" + delete-branch: true + title: "[bot] Execute example notebooks" + # language=Markdown + body: > + This PR was [automatically generated] to re-execute + the example notebooks for use in the documentation. + + [automatically generated]: https://github.com/Econ-ARK/HARK/actions/workflows/execute-notebooks.yml diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 000000000..21fba1c97 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,27 @@ +name: pre-commit + +on: [push, pull_request] + +jobs: + format: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ["3.10"] + + steps: + - uses: actions/checkout@v4 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + + - name: Install packages + run: | + python -m pip install --upgrade pip + python -m pip install ".[dev]" + pip list + + - name: Lint + run: pre-commit run --all-files --show-diff-on-failure --color always diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1acc0a54e..30117c837 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,13 +1,14 @@ exclude: Documentation/example_notebooks/ repos: - - repo: https://github.com/mwouts/jupytext - rev: v1.15.0 + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.1.4 hooks: - - id: jupytext - args: [--sync, --set-formats, "ipynb", --pipe, black, --execute] - additional_dependencies: [jupytext, black, nbconvert] - files: ^examples/.*\.ipynb$ + - id: ruff + types_or: [jupyter] + - id: ruff-format + args: [--check] + types_or: [jupyter] - repo: https://github.com/psf/black rev: 23.7.0 diff --git a/Documentation/_static/override-nbsphinx-gallery.css b/Documentation/_static/override-nbsphinx-gallery.css index 65ee21fe6..c020c4b0e 100644 --- a/Documentation/_static/override-nbsphinx-gallery.css +++ b/Documentation/_static/override-nbsphinx-gallery.css @@ -1,3 +1,3 @@ .nbsphinx-gallery { - grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); + grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); } diff --git a/Documentation/conf.py b/Documentation/conf.py index 044ca72af..3edbc487e 100644 --- a/Documentation/conf.py +++ b/Documentation/conf.py @@ -1,17 +1,19 @@ -from datetime import date import warnings +from datetime import date try: import numba except ImportError: pass else: - warnings.filterwarnings("ignore", - message="numba.generated_jit.*", - category=numba.NumbaDeprecationWarning) - warnings.filterwarnings("ignore", - message=".* 'nopython' .*", - category=numba.NumbaDeprecationWarning) + warnings.filterwarnings( + "ignore", + message="numba.generated_jit.*", + category=numba.NumbaDeprecationWarning, + ) + warnings.filterwarnings( + "ignore", message=".* 'nopython' .*", category=numba.NumbaDeprecationWarning + ) # Project information project = "HARK" @@ -64,7 +66,7 @@ html_theme = "pydata_sphinx_theme" html_static_path = ["_static"] html_css_files = [ - 'override-nbsphinx-gallery.css', + "override-nbsphinx-gallery.css", ] html_theme_options = { @@ -95,7 +97,7 @@ "type": "local", "attributes": {"target": "_blank"}, }, - ] + ], } # Point to Econ-ARK repo for edit buttons diff --git a/Documentation/overview/ARKitecture.md b/Documentation/overview/ARKitecture.md index b087250df..94aeeaef7 100644 --- a/Documentation/overview/ARKitecture.md +++ b/Documentation/overview/ARKitecture.md @@ -45,7 +45,6 @@ After you [installed](https://docs.econ-ark.org/guides/quick_start.html) and [cl HARK's root directory contains six tool modules, [^1] each containing a variety of functions and classes that can be used in many economic models-- or even for mathematical purposes that have nothing to do with economics. Some of the tool modules are very sparely populated at this time, while others are quite large. We expect that all of these modules will grow considerably in the near future, as new tools are ''low hanging fruit'' for contribution to the project. [^2] [^1]: The ''taxonomy'' of these modules is in flux; the functions described here could be combined into fewer modules or further divided by purpose. - [^2]: That is, as the foundational, building-block elements of HARK, new tools are not difficult to program and do not require extensive integration with many moving parts. #### HARK.core @@ -87,7 +86,6 @@ Methods for optimizing an objective function for the purposes of estimating a mo By default, processes in Python are single-threaded, using only a single CPU core. The **_HARK.parallel_** module provides basic tools for using multiple CPU cores simultaneously, with minimal effort. [^4] In particular, it provides the function **_multiThreadCommands_**, which takes two arguments: a list of **_AgentType_**s and a list of commands as strings; each command should be a method of the **_AgentType_**s. The function simply distributes the **_AgentType_**s across threads on different cores and executes each command in order, returning no output (the **_AgentType_**s themselves are changed by running the commands). Equivalent results would be achieved by simply looping over each type and running each method in the list. Indeed, **_HARK.parallel_** also has a function called **_multiThreadCommandsFake_** that does just that, with identical syntax to **_multiThreadCommands_**; multithreading in HARK can thus be easily turned on and off. [^5] The module also has functions for a parallel implementation of the Nelder-Mead simplex algorithm, as described in Wiswall and Lee (2011). See [here](https://docs.econ-ark.org/reference/tools/parallel.html) for full documentation. [^4]: **_HARK.parallel_** uses two packages that aren't included in the default distribution of Anaconda: **_joblib_** and **_dill_**; see [here](https://docs.econ-ark.org/guides/quick_start.html#using-hark-with-anaconda) for instructions on how to install them. - [^5]: In the future, **_HARK.parallel_** might be absorbed into **_HARK.core_** and **_HARK.estimation_**, particularly if **_joblib_** and **_dill_** become part of the standard Anaconda distribution. ### AgentType Class diff --git a/HARK/Calibration/Income/tests/test_IncomeTools.py b/HARK/Calibration/Income/tests/test_IncomeTools.py index a8f6f5aff..5438be385 100644 --- a/HARK/Calibration/Income/tests/test_IncomeTools.py +++ b/HARK/Calibration/Income/tests/test_IncomeTools.py @@ -203,7 +203,7 @@ def test_Cagetti(self): age_max=age_max, adjust_infl_to=adjust_infl_to, start_year=start_year, - **spec + **spec, ) MeanP = find_profile(params["PermGroFac"], params["P0"]) diff --git a/HARK/ConsumptionSaving/ConsAggShockModel.py b/HARK/ConsumptionSaving/ConsAggShockModel.py index 89a4f9ae0..2d14f81c7 100644 --- a/HARK/ConsumptionSaving/ConsAggShockModel.py +++ b/HARK/ConsumptionSaving/ConsAggShockModel.py @@ -144,7 +144,7 @@ def __init__(self, **kwds): self, solution_terminal=deepcopy(IndShockConsumerType.solution_terminal_), pseudo_terminal=False, - **params + **params, ) # Add consumer-type specific objects, copying to create independent versions @@ -2441,7 +2441,7 @@ def __init__( "KtoLnow", "Mrkv", # This one is new ], - **kwds + **kwds, ): agents = agents if agents is not None else list() params = init_mrkv_cobb_douglas.copy() @@ -2453,7 +2453,7 @@ def __init__( tolerance=tolerance, act_T=act_T, sow_vars=sow_vars, - **params + **params, ) self.sow_init["Mrkv"] = params["MrkvNow_init"] @@ -2885,7 +2885,7 @@ def __init__(self, agents=None, tolerance=0.0001, **kwds): reap_vars=["aNow", "EmpNow"], track_vars=["Mrkv", "Aprev", "Mnow", "Urate"], dyn_vars=["AFunc"], - **params + **params, ) self.update() diff --git a/HARK/ConsumptionSaving/ConsIndShockModel.py b/HARK/ConsumptionSaving/ConsIndShockModel.py index 37ceaff43..693089d5b 100644 --- a/HARK/ConsumptionSaving/ConsIndShockModel.py +++ b/HARK/ConsumptionSaving/ConsIndShockModel.py @@ -709,7 +709,7 @@ def set_and_update_values(self, solution_next, IncShkDstn, LivPrb, DiscFac): try: self.MPCminNow = 1.0 / (1.0 + self.PatFac / solution_next.MPCmin) except: - self.MPCminNow = 0.0 + self.MPCminNow = 0.0 self.Ex_IncNext = np.dot( self.ShkPrbsNext, self.TranShkValsNext * self.PermShkValsNext ) @@ -1604,7 +1604,7 @@ def __init__(self, verbose=1, quiet=False, **kwds): self, solution_terminal=deepcopy(self.solution_terminal_), pseudo_terminal=False, - **kwds + **kwds, ) # Add consumer-type specific objects, copying to create independent versions @@ -1820,7 +1820,7 @@ def get_shocks(self): PermGroFac = np.array(self.PermGroFac) # Cycle time has already been advanced self.shocks["PermShk"] = PermGroFac[self.t_cycle - 1] - #self.shocks["PermShk"][self.t_cycle == 0] = 1. # Add this at some point + # self.shocks["PermShk"][self.t_cycle == 0] = 1. # Add this at some point self.shocks["TranShk"] = np.ones(self.AgentCount) def get_Rfree(self): @@ -1930,19 +1930,19 @@ def log_condition_result(self, name, result, message, verbose): self.conditions[name] = result set_verbosity_level((4 - verbose) * 10) _log.info(message) - self.bilt['conditions_report'] += message + '\n' + self.bilt["conditions_report"] += message + "\n" def check_AIC(self, verbose=None): """ Evaluate and report on the Absolute Impatience Condition. """ name = "AIC" - APFac = self.bilt['APFac'] - result = APFac < 1. + APFac = self.bilt["APFac"] + result = APFac < 1.0 messages = { True: f"APFac={APFac:.5f} : The Absolute Patience Factor satisfies the Absolute Impatience Condition (AIC) Þ < 1.", - False: f"APFac={APFac:.5f} : The Absolute Patience Factor violates the Absolute Impatience Condition (AIC) Þ < 1." + False: f"APFac={APFac:.5f} : The Absolute Patience Factor violates the Absolute Impatience Condition (AIC) Þ < 1.", } verbose = self.verbose if verbose is None else verbose self.log_condition_result(name, result, messages[result], verbose) @@ -1952,13 +1952,12 @@ def check_GICRaw(self, verbose=None): Evaluate and report on the Growth Impatience Condition for the Perfect Foresight model. """ name = "GICRaw" - GPFacRaw = self.bilt['GPFacRaw'] - result = GPFacRaw < 1. + GPFacRaw = self.bilt["GPFacRaw"] + result = GPFacRaw < 1.0 messages = { True: f"GPFacRaw={GPFacRaw:.5f} : The Growth Patience Factor satisfies the Growth Impatience Condition (GICRaw) Þ/G < 1.", - False: f"GPFacRaw={GPFacRaw:.5f} : The Growth Patience Factor violates the Growth Impatience Condition (GICRaw) Þ/G < 1." - + False: f"GPFacRaw={GPFacRaw:.5f} : The Growth Patience Factor violates the Growth Impatience Condition (GICRaw) Þ/G < 1.", } verbose = self.verbose if verbose is None else verbose self.log_condition_result(name, result, messages[result], verbose) @@ -1968,13 +1967,13 @@ def check_RIC(self, verbose=None): Evaluate and report on the Return Impatience Condition. """ name = "RIC" - RPFac = self.bilt['RPFac'] - result = RPFac < 1. + RPFac = self.bilt["RPFac"] + result = RPFac < 1.0 messages = { True: f"RPFac={RPFac:.5f} : The Return Patience Factor satisfies the Return Impatience Condition (RIC) Þ/R < 1.", - False: f"RPFac={RPFac:.5f} : The Return Patience Factor violates the Return Impatience Condition (RIC) Þ/R < 1." - } + False: f"RPFac={RPFac:.5f} : The Return Patience Factor violates the Return Impatience Condition (RIC) Þ/R < 1.", + } verbose = self.verbose if verbose is None else verbose self.log_condition_result(name, result, messages[result], verbose) @@ -1983,33 +1982,33 @@ def check_FHWC(self, verbose=None): Evaluate and report on the Finite Human Wealth Condition. """ name = "FHWC" - FHWFac = self.bilt['FHWFac'] - result = FHWFac < 1. + FHWFac = self.bilt["FHWFac"] + result = FHWFac < 1.0 messages = { True: f"FHWFac={FHWFac:.5f} : The Finite Human Wealth Factor satisfies the Finite Human Wealth Condition (FHWC) G/R < 1.", - False: f"FHWFac={FHWFac:.5f} : The Finite Human Wealth Factor violates the Finite Human Wealth Condition (FHWC) G/R < 1." + False: f"FHWFac={FHWFac:.5f} : The Finite Human Wealth Factor violates the Finite Human Wealth Condition (FHWC) G/R < 1.", } verbose = self.verbose if verbose is None else verbose self.log_condition_result(name, result, messages[result], verbose) - + def check_FVAC(self, verbose=None): """ Evaluate and report on the Finite Value of Autarky Condition under perfect foresight. """ name = "PFFVAC" - PFVAFac = self.bilt['PFVAFac'] - result = PFVAFac < 1. + PFVAFac = self.bilt["PFVAFac"] + result = PFVAFac < 1.0 messages = { True: f"PFVAFac={PFVAFac:.5f} : The Finite Value of Autarky Factor satisfies the Finite Value of Autarky Condition βG^(1-ρ) < 1.", - False: f"PFVAFac={PFVAFac:.5f} : The Finite Value of Autarky Factor violates the Finite Value of Autarky Condition βG^(1-ρ) < 1." + False: f"PFVAFac={PFVAFac:.5f} : The Finite Value of Autarky Factor violates the Finite Value of Autarky Condition βG^(1-ρ) < 1.", } verbose = self.verbose if verbose is None else verbose self.log_condition_result(name, result, messages[result], verbose) - + def describe_parameters(self): - ''' + """ Make a string describing this instance's parameter values, including their representation in code and symbolically. @@ -2017,40 +2016,47 @@ def describe_parameters(self): ------- param_desc : str Description of parameters as a unicode string. - ''' + """ params_to_describe = [ - #[name, description, symbol, time varying] - ['DiscFac', 'intertemporal discount factor', 'β',False], - ['Rfree', 'risk free interest factor', 'R',False], - ['PermGroFac', 'permanent income growth factor', 'G',True], - ['CRRA', 'coefficient of relative risk aversion','ρ',False], - ['LivPrb', 'survival probability','ℒ',True], - ['APFac', 'absolute patience factor', 'Þ=(βℒR)^(1/ρ)',False] + # [name, description, symbol, time varying] + ["DiscFac", "intertemporal discount factor", "β", False], + ["Rfree", "risk free interest factor", "R", False], + ["PermGroFac", "permanent income growth factor", "G", True], + ["CRRA", "coefficient of relative risk aversion", "ρ", False], + ["LivPrb", "survival probability", "ℒ", True], + ["APFac", "absolute patience factor", "Þ=(βℒR)^(1/ρ)", False], ] - - param_desc = '' + + param_desc = "" for j in range(len(params_to_describe)): this_entry = params_to_describe[j] if this_entry[3]: - val = getattr(self,this_entry[0])[0] + val = getattr(self, this_entry[0])[0] else: try: - val = getattr(self,this_entry[0]) + val = getattr(self, this_entry[0]) except: val = self.bilt[this_entry[0]] - this_line = this_entry[2] + f'={val:.5f} : ' + this_entry[1] + ' (' + this_entry[0] + ')\n' + this_line = ( + this_entry[2] + + f"={val:.5f} : " + + this_entry[1] + + " (" + + this_entry[0] + + ")\n" + ) param_desc += this_line - + return param_desc - + def calc_limiting_values(self): - ''' + """ Compute various scalar values that are relevant to characterizing the solution to an infinite horizon problem. This method should only be called when T_cycle=1 and cycles=0, otherwise the values generated are meaningless. This method adds the following values to the instance in the dictionary attribute auxiliary. - + APFac : Absolute Patience Factor GPFacRaw : Growth Patience Factor FHWFac : Finite Human Wealth Factor @@ -2064,26 +2070,34 @@ def calc_limiting_values(self): Returns ------- None - ''' + """ aux_dict = self.bilt - aux_dict['APFac'] = (self.Rfree * self.DiscFac * self.LivPrb[0]) ** (1 / self.CRRA) - aux_dict['GPFacRaw'] = aux_dict['APFac'] / self.PermGroFac[0] - aux_dict['FHWFac'] = self.PermGroFac[0] / self.Rfree - aux_dict['RPFac'] = aux_dict['APFac'] / self.Rfree - aux_dict['PFVAFac'] = (self.DiscFac * self.LivPrb[0]) * self.PermGroFac[0]**(1. - self.CRRA) - aux_dict['cNrmPDV'] = 1. / (1. - aux_dict['RPFac']) - aux_dict['MPCmin'] = np.maximum(1. - aux_dict['RPFac'], 0.) - constrained = hasattr(self, "BoroCnstArt") and (self.BoroCnstArt is not None) and (self.BoroCnstArt > -np.inf) - + aux_dict["APFac"] = (self.Rfree * self.DiscFac * self.LivPrb[0]) ** ( + 1 / self.CRRA + ) + aux_dict["GPFacRaw"] = aux_dict["APFac"] / self.PermGroFac[0] + aux_dict["FHWFac"] = self.PermGroFac[0] / self.Rfree + aux_dict["RPFac"] = aux_dict["APFac"] / self.Rfree + aux_dict["PFVAFac"] = (self.DiscFac * self.LivPrb[0]) * self.PermGroFac[0] ** ( + 1.0 - self.CRRA + ) + aux_dict["cNrmPDV"] = 1.0 / (1.0 - aux_dict["RPFac"]) + aux_dict["MPCmin"] = np.maximum(1.0 - aux_dict["RPFac"], 0.0) + constrained = ( + hasattr(self, "BoroCnstArt") + and (self.BoroCnstArt is not None) + and (self.BoroCnstArt > -np.inf) + ) + if constrained: - aux_dict['MPCmax'] = 1. + aux_dict["MPCmax"] = 1.0 else: - aux_dict['MPCmax'] = aux_dict['MPCmin'] - if aux_dict['FHWFac'] < 1.: - aux_dict['hNrm'] = 1. / (1. - aux_dict['FHWFac']) + aux_dict["MPCmax"] = aux_dict["MPCmin"] + if aux_dict["FHWFac"] < 1.0: + aux_dict["hNrm"] = 1.0 / (1.0 - aux_dict["FHWFac"]) else: - aux_dict['hNrm'] = np.inf - + aux_dict["hNrm"] = np.inf + self.bilt = aux_dict def check_conditions(self, verbose=None): @@ -2111,17 +2125,17 @@ def check_conditions(self, verbose=None): None """ self.conditions = {} - self.bilt['conditions_report'] = '' + self.bilt["conditions_report"] = "" self.degenerate = False verbose = self.verbose if verbose is None else verbose # This method only checks for the conditions for infinite horizon models # with a 1 period cycle. If these conditions are not met, we exit early. if self.cycles != 0 or self.T_cycle > 1: - trivial_message = 'No conditions report was produced because this functionality is only supported for infinite horizon models with a cycle length of 1.' + trivial_message = "No conditions report was produced because this functionality is only supported for infinite horizon models with a cycle length of 1." self.log_condition_result(None, None, trivial_message, verbose) if not self.quiet: - _log.info(self.bilt['conditions_report']) + _log.info(self.bilt["conditions_report"]) return # Calculate some useful quantities that will be used in the condition checks @@ -2135,71 +2149,77 @@ def check_conditions(self, verbose=None): self.check_GICRaw(verbose) self.check_FVAC(verbose) self.check_FHWC(verbose) - constrained = hasattr(self, "BoroCnstArt") and (self.BoroCnstArt is not None) and (self.BoroCnstArt > -np.inf) - + constrained = ( + hasattr(self, "BoroCnstArt") + and (self.BoroCnstArt is not None) + and (self.BoroCnstArt > -np.inf) + ) + # Exit now if verbose output was not requested. if not verbose: if not self.quiet: - _log.info(self.bilt['conditions_report']) + _log.info(self.bilt["conditions_report"]) return - + # Report on the degeneracy of the consumption function solution if not constrained: - if self.conditions['FHWC']: - RIC_message = '\nBecause the FHWC is satisfied, the solution is not c(m)=Infinity.' - if self.conditions['RIC']: + if self.conditions["FHWC"]: + RIC_message = "\nBecause the FHWC is satisfied, the solution is not c(m)=Infinity." + if self.conditions["RIC"]: RIC_message += " Because the RIC is also satisfied, the solution is also not c(m)=0 for all m, so a non-degenerate linear solution exists." degenerate = False else: RIC_message += " However, because the RIC is violated, the solution is degenerate at c(m) = 0 for all m." degenerate = True else: - RIC_message = '\nBecause the FHWC condition is violated and the consumer is not constrained, the solution is degenerate at c(m)=Infinity.' + RIC_message = "\nBecause the FHWC condition is violated and the consumer is not constrained, the solution is degenerate at c(m)=Infinity." degenerate = True else: - if self.conditions['RIC']: + if self.conditions["RIC"]: RIC_message = "\nBecause the RIC is satisfied and the consumer is constrained, the solution is not c(m)=0 for all m." - if self.conditions['GICRaw']: + if self.conditions["GICRaw"]: RIC_message += " Because the GICRaw is also satisfied, the solution is non-degenerate. It is piecewise linear with an infinite number of kinks, approaching the unconstrained solution as m goes to infinity." degenerate = False else: RIC_message += " Because the GICRaw is violated, the solution is non-degenerate. It is piecewise linear with a single kink at some 0 < m < 1; it equals the unconstrained solution above that kink point and has c(m) = m below it." degenerate = False else: - if self.conditions['GICRaw']: + if self.conditions["GICRaw"]: RIC_message = "\nBecause the RIC is violated but the GIC is satisfied, the FHWC is necessarily also violated. In this case, the consumer's pathological patience is offset by his infinite human wealth, against which he cannot borrow arbitrarily; a non-degenerate solution exists." degenerate = False else: - RIC_message = '\nBecause the RIC is violated but the FHWC is satisfied, the solution is degenerate at c(m)=0 for all m.' + RIC_message = "\nBecause the RIC is violated but the FHWC is satisfied, the solution is degenerate at c(m)=0 for all m." degenerate = True self.log_condition_result(None, None, RIC_message, verbose) - - if degenerate: # All of the other checks are meaningless if the solution is degenerate + + if ( + degenerate + ): # All of the other checks are meaningless if the solution is degenerate if not self.quiet: - _log.info(self.bilt['conditions_report']) - return - + _log.info(self.bilt["conditions_report"]) + return + # Report on the consequences of the Absolute Impatience Condition - if self.conditions['AIC']: + if self.conditions["AIC"]: AIC_message = "\nBecause the AIC is satisfied, the absolute amount of consumption is expected to fall over time." else: AIC_message = "\nBecause the AIC is violated, the absolute amount of consumption is expected to grow over time." self.log_condition_result(None, None, AIC_message, verbose) - + # Report on the consequences of the Growth Impatience Condition - if self.conditions['GICRaw']: + if self.conditions["GICRaw"]: GIC_message = "\nBecause the GICRaw is satisfed, the ratio of individual wealth to permanent income is expected to fall indefinitely." - elif self.conditions['FHWC']: + elif self.conditions["FHWC"]: "\nBecause the GICRaw is violated but the FHWC is satisfied, the ratio of individual wealth to permanent income is expected to rise toward infinity." else: pass # This can never be reached! If GICRaw and FHWC both fail, then the RIC also fails, and we would have exited by this point. self.log_condition_result(None, None, GIC_message, verbose) - + if not self.quiet: - _log.info(self.bilt['conditions_report']) - - + _log.info(self.bilt["conditions_report"]) + + # Make a dictionary to specify an idiosyncratic income shocks consumer init_idiosyncratic_shocks = dict( init_perfect_foresight, @@ -2234,7 +2254,7 @@ def check_conditions(self, verbose=None): # Use permanent income neutral measure (see Harmenberg 2021) during simulations when True. # Whether Newborns have transitory shock. The default is False. "NewbornTransShk": False, - } + }, ) @@ -2350,17 +2370,17 @@ def reset_rng(self): if hasattr(self, "IncShkDstn"): for dstn in self.IncShkDstn: dstn.reset() - + def post_solve(self): """ Method that is run automatically at the end of a call to solve. Here, it simply calls calc_stable_points() if appropriate: an infinite horizon problem with a single repeated period in its cycle. - + Parameters ---------- None - + Returns ------- None @@ -2436,7 +2456,6 @@ def get_shocks(self): self.shocks["PermShk"] = PermShkNow self.shocks["TranShk"] = TranShkNow - def define_distribution_grid( self, dist_mGrid=None, @@ -3024,24 +3043,24 @@ def calc_jacobian(self, shk_param, T): ######## # STEP4 # of the algorithm ######## - + # Function to compute jacobian matrix from fake news matrix def J_from_F(F): J = F.copy() for t in range(1, F.shape[0]): - J[1:, t] += J[:-1, t-1] + J[1:, t] += J[:-1, t - 1] return J - + J_A = J_from_F(Curl_F_A) J_C = J_from_F(Curl_F_C) - + ######## # Additional step due to compute Zeroth Column of the Jacobian - ######## - + ######## + params = deepcopy(self.__dict__["parameters"]) - params["T_cycle"] = 2 # Dimension of Jacobian Matrix - + params["T_cycle"] = 2 # Dimension of Jacobian Matrix + params["LivPrb"] = params["T_cycle"] * [self.LivPrb[0]] params["PermGroFac"] = params["T_cycle"] * [self.PermGroFac[0]] params["PermShkStd"] = params["T_cycle"] * [self.PermShkStd[0]] @@ -3049,13 +3068,13 @@ def J_from_F(F): params["Rfree"] = params["T_cycle"] * [self.Rfree] params["UnempPrb"] = params["T_cycle"] * [self.UnempPrb] params["IncUnemp"] = params["T_cycle"] * [self.IncUnemp] - params['IncShkDstn'] = params['T_cycle']* [self.IncShkDstn[0]] - params['cFunc_terminal_'] = deepcopy(self.solution[0].cFunc) - + params["IncShkDstn"] = params["T_cycle"] * [self.IncShkDstn[0]] + params["cFunc_terminal_"] = deepcopy(self.solution[0].cFunc) + # Create instance of a finite horizon agent for calculation of zeroth ZerothColAgent = IndShockConsumerType(**params) ZerothColAgent.cycles = 1 # required - + # If parameter is in time invariant list then add it to time vary list ZerothColAgent.del_from_time_inv(shk_param) ZerothColAgent.add_to_time_vary(shk_param) @@ -3065,20 +3084,22 @@ def J_from_F(F): # Solve ZerothColAgent.solve() - + # this condition is because some attributes are specified as lists while other as floats if type(getattr(self, shk_param)) == list: - peturbed_list = ( - [getattr(self, shk_param)[0] + dx] - + (params["T_cycle"] - 1) * [getattr(self, shk_param)[0]] - ) # Sequence of interest rates the agent faces + peturbed_list = [getattr(self, shk_param)[0] + dx] + ( + params["T_cycle"] - 1 + ) * [ + getattr(self, shk_param)[0] + ] # Sequence of interest rates the agent faces else: - peturbed_list = ( - [getattr(self, shk_param) + dx] - + (params["T_cycle"] - 1) * [getattr(self, shk_param)] - ) # Sequence of interest rates the agent - - setattr(ZerothColAgent, shk_param, peturbed_list) # Set attribute to agent + peturbed_list = [getattr(self, shk_param) + dx] + ( + params["T_cycle"] - 1 + ) * [ + getattr(self, shk_param) + ] # Sequence of interest rates the agent + + setattr(ZerothColAgent, shk_param, peturbed_list) # Set attribute to agent # Use Harmenberg Neutral Measure ZerothColAgent.neutral_measure = True @@ -3087,28 +3108,26 @@ def J_from_F(F): # Calculate Transition Matrices ZerothColAgent.define_distribution_grid() ZerothColAgent.calc_transition_matrix() - + tranmat_t_zeroth_col = ZerothColAgent.tran_matrix dstn_t_zeroth_col = self.vec_erg_dstn.T[0] - + C_t_no_sim = np.zeros(T) A_t_no_sim = np.zeros(T) for i in range(T): - if i ==0: - dstn_t_zeroth_col = np.dot(tranmat_t_zeroth_col[i],dstn_t_zeroth_col) + if i == 0: + dstn_t_zeroth_col = np.dot(tranmat_t_zeroth_col[i], dstn_t_zeroth_col) else: - dstn_t_zeroth_col = np.dot(tranmat_ss,dstn_t_zeroth_col) - - C_t_no_sim[i] = np.dot(self.cPol_Grid ,dstn_t_zeroth_col) - A_t_no_sim[i] = np.dot( self.aPol_Grid ,dstn_t_zeroth_col) - - J_A.T[0] = (A_t_no_sim - self.A_ss)/dx - J_C.T[0] = (C_t_no_sim - self.C_ss)/dx - - return J_C, J_A + dstn_t_zeroth_col = np.dot(tranmat_ss, dstn_t_zeroth_col) + + C_t_no_sim[i] = np.dot(self.cPol_Grid, dstn_t_zeroth_col) + A_t_no_sim[i] = np.dot(self.aPol_Grid, dstn_t_zeroth_col) + J_A.T[0] = (A_t_no_sim - self.A_ss) / dx + J_C.T[0] = (C_t_no_sim - self.C_ss) / dx + return J_C, J_A def make_euler_error_func(self, mMax=100, approx_inc_dstn=True): """ @@ -3221,13 +3240,12 @@ def pre_solve(self): self.update_solution_terminal() if not self.quiet: self.check_conditions(verbose=self.verbose) - - + def describe_parameters(self): - ''' + """ Generate a string describing the primitive model parameters that will be used to calculating limiting values and factors. - + Parameters ---------- None @@ -3236,33 +3254,44 @@ def describe_parameters(self): ------- param_desc : str Description of primitive parameters. - ''' + """ # Get parameter description from the perfect foresight model param_desc = PerfForesightConsumerType.describe_parameters(self) - + # Make a new entry for weierstrass-p (the weird formatting here is to # make it easier to adapt into the style of the superclass if we add more # parameter reports later) - this_entry = ['WorstPrb', 'probability of worst income shock realization', '℘', False] + this_entry = [ + "WorstPrb", + "probability of worst income shock realization", + "℘", + False, + ] try: - val = getattr(self,this_entry[0]) + val = getattr(self, this_entry[0]) except: - val = self.bilt[this_entry[0]] - this_line = this_entry[2] + f'={val:.5f} : ' + this_entry[1] + ' (' + this_entry[0] + ')\n' - + val = self.bilt[this_entry[0]] + this_line = ( + this_entry[2] + + f"={val:.5f} : " + + this_entry[1] + + " (" + + this_entry[0] + + ")\n" + ) + # Add in the new entry and return it param_desc += this_line return param_desc - - + def calc_limiting_values(self): - ''' + """ Compute various scalar values that are relevant to characterizing the solution to an infinite horizon problem. This method should only be called when T_cycle=1 and cycles=0, otherwise the values generated are meaningless. This method adds the following values to this instance in the dictionary attribute auxiliary. - + APFac : Absolute Patience Factor GPFacRaw : Growth Patience Factor GPFacMod : Risk-Modified Growth Patience Factor @@ -3284,39 +3313,43 @@ def calc_limiting_values(self): Returns ------- None - ''' + """ PerfForesightConsumerType.calc_limiting_values(self) aux_dict = self.bilt - + # Calculate the risk-modified growth impatience factor PermShkDstn = self.PermShkDstn[0] - inv_func = lambda x : x**(-1.) - GroCompPermShk = expected(inv_func, PermShkDstn)[0]**(-1.) - aux_dict['GPFacMod'] = aux_dict['APFac'] / (self.PermGroFac[0] * GroCompPermShk) - + inv_func = lambda x: x ** (-1.0) + GroCompPermShk = expected(inv_func, PermShkDstn)[0] ** (-1.0) + aux_dict["GPFacMod"] = aux_dict["APFac"] / (self.PermGroFac[0] * GroCompPermShk) + # Calculate the mortality-adjusted growth impatience factor (and version # with Modigiliani bequests) - aux_dict['GPFacLiv'] = aux_dict['GPFacRaw'] * self.LivPrb[0] - aux_dict['GPFacLivMod'] = aux_dict['GPFacLiv'] * self.LivPrb[0] - + aux_dict["GPFacLiv"] = aux_dict["GPFacRaw"] * self.LivPrb[0] + aux_dict["GPFacLivMod"] = aux_dict["GPFacLiv"] * self.LivPrb[0] + # Calculate the risk-modified value of autarky factor - if self.CRRA == 1.: + if self.CRRA == 1.0: UtilCompPermShk = np.exp(expected(np.log, PermShkDstn)[0]) else: - CRRAfunc = lambda x : x**(1.-self.CRRA) - UtilCompPermShk = expected(CRRAfunc, PermShkDstn)[0]**(1/(1.-self.CRRA)) - aux_dict['VAFac'] = self.DiscFac*(self.PermGroFac[0]*UtilCompPermShk)**(1.-self.CRRA) - + CRRAfunc = lambda x: x ** (1.0 - self.CRRA) + UtilCompPermShk = expected(CRRAfunc, PermShkDstn)[0] ** ( + 1 / (1.0 - self.CRRA) + ) + aux_dict["VAFac"] = self.DiscFac * (self.PermGroFac[0] * UtilCompPermShk) ** ( + 1.0 - self.CRRA + ) + # Calculate the expected log permanent income shock, which will be used # for the Szeidl variation of the Growth Impatience condition - aux_dict['ELogPermShk'] = expected(np.log, PermShkDstn)[0] - + aux_dict["ELogPermShk"] = expected(np.log, PermShkDstn)[0] + # Calculate the Harmenberg permanent income neutral expected log permanent # shock and the Harmenberg Growth Patience Factor - Hrm_func = lambda x : x * np.log(x) + Hrm_func = lambda x: x * np.log(x) PermShk_Hrm = np.exp(expected(Hrm_func, PermShkDstn)[0]) - aux_dict['GPFacHrm'] = aux_dict['GPFacRaw'] / PermShk_Hrm - + aux_dict["GPFacHrm"] = aux_dict["GPFacRaw"] / PermShk_Hrm + # Calculate the probability of the worst income shock realization PermShkValsNext = self.IncShkDstn[0].atoms[0] TranShkValsNext = self.IncShkDstn[0].atoms[1] @@ -3328,17 +3361,17 @@ def calc_limiting_values(self): WorstIncPrb = np.sum( ShkPrbsNext[(PermShkValsNext * TranShkValsNext) == WorstIncNext] ) - aux_dict['WorstPrb'] = WorstIncPrb - + aux_dict["WorstPrb"] = WorstIncPrb + # Calculate the weak return patience factor - aux_dict['WRPFac'] = WorstIncPrb**(1./self.CRRA) * aux_dict['RPFac'] - + aux_dict["WRPFac"] = WorstIncPrb ** (1.0 / self.CRRA) * aux_dict["RPFac"] + # Calculate human wealth and the infinite horizon natural borrowing constraint - if aux_dict['FHWFac'] < 1.: - hNrm = Ex_IncNext / (1. - aux_dict['FHWFac']) + if aux_dict["FHWFac"] < 1.0: + hNrm = Ex_IncNext / (1.0 - aux_dict["FHWFac"]) else: hNrm = np.inf - temp = PermShkMinNext * aux_dict['FHWFac'] + temp = PermShkMinNext * aux_dict["FHWFac"] BoroCnstNat = -TranShkMinNext * temp / (1.0 - temp) # Find the upper bound of the MPC as market resources approach the minimum @@ -3346,111 +3379,104 @@ def calc_limiting_values(self): if BoroCnstNat < BoroCnstArt: MPCmax = 1.0 # if natural borrowing constraint is overridden by artificial one, MPCmax is 1 else: - MPCmax = 1.0 - WorstIncPrb ** (1.0 / self.CRRA) * aux_dict['RPFac'] + MPCmax = 1.0 - WorstIncPrb ** (1.0 / self.CRRA) * aux_dict["RPFac"] MPCmax = np.maximum(MPCmax, 0.0) - + # Store maximum MPC and human wealth - aux_dict['hNrm'] = hNrm - aux_dict['MPCmax'] = MPCmax - - self.bilt = aux_dict + aux_dict["hNrm"] = hNrm + aux_dict["MPCmax"] = MPCmax + self.bilt = aux_dict def check_GICMod(self, verbose=None): """ Evaluate and report on the Risk-Modified Growth Impatience Condition. """ name = "GICMod" - GPFacMod = self.bilt['GPFacMod'] - result = GPFacMod < 1. + GPFacMod = self.bilt["GPFacMod"] + result = GPFacMod < 1.0 messages = { True: f"GPFacMod={GPFacMod:.5f} : The Risk-Modified Growth Patience Factor satisfies the Risk-Modified Growth Impatience Condition (GICMod) Þ/(G‖Ψ‖_(-1)) < 1.", - False: f"GPFacMod={GPFacMod:.5f} : The Risk-Modified Growth Patience Factor violates the Risk-Modified Growth Impatience Condition (GICMod) Þ/(G‖Ψ‖_(-1)) < 1." + False: f"GPFacMod={GPFacMod:.5f} : The Risk-Modified Growth Patience Factor violates the Risk-Modified Growth Impatience Condition (GICMod) Þ/(G‖Ψ‖_(-1)) < 1.", } verbose = self.verbose if verbose is None else verbose self.log_condition_result(name, result, messages[result], verbose) - def check_GICSdl(self, verbose=None): """ Evaluate and report on the Szeidl variation of the Growth Impatience Condition. """ name = "GICSdl" - ELogPermShk = self.bilt['ELogPermShk'] - result = np.log(self.bilt['GPFacRaw']) < ELogPermShk + ELogPermShk = self.bilt["ELogPermShk"] + result = np.log(self.bilt["GPFacRaw"]) < ELogPermShk messages = { True: f"E[log Ψ]={ELogPermShk:.5f} : The expected log permanent income shock satisfies the Szeidl Growth Impatience Condition (GICSdl) log(Þ/G) < E[log Ψ].", - False: f"E[log Ψ]={ELogPermShk:.5f} : The expected log permanent income shock violates the Szeidl Growth Impatience Condition (GICSdl) log(Þ/G) < E[log Ψ]." + False: f"E[log Ψ]={ELogPermShk:.5f} : The expected log permanent income shock violates the Szeidl Growth Impatience Condition (GICSdl) log(Þ/G) < E[log Ψ].", } verbose = self.verbose if verbose is None else verbose self.log_condition_result(name, result, messages[result], verbose) - - + def check_GICHrm(self, verbose=None): """ Evaluate and report on the Harmenberg variation of the Growth Impatience Condition. """ name = "GICHrm" - GPFacHrm = self.bilt['GPFacHrm'] - result = GPFacHrm < 1. + GPFacHrm = self.bilt["GPFacHrm"] + result = GPFacHrm < 1.0 messages = { True: f"GPFacHrm={GPFacHrm:.5f} : The Harmenberg Expected Growth Patience Factor satisfies the Harmenberg Growth Normalized Impatience Condition (GICHrm) Þ/G < exp(E[Ψlog Ψ]).", - False: f"GPFacHrm={GPFacHrm:.5f} : The Harmenberg Expected Growth Patience Factor violates the Harmenberg Growth Normalized Impatience Condition (GICHrm) Þ/G < exp(E[Ψlog Ψ])." + False: f"GPFacHrm={GPFacHrm:.5f} : The Harmenberg Expected Growth Patience Factor violates the Harmenberg Growth Normalized Impatience Condition (GICHrm) Þ/G < exp(E[Ψlog Ψ]).", } verbose = self.verbose if verbose is None else verbose self.log_condition_result(name, result, messages[result], verbose) - - + def check_GICLiv(self, verbose=None): """ Evaluate and report on the Mortality-Adjusted Growth Impatience Condition. """ name = "GICLiv" - GPFacLiv = self.bilt['GPFacLiv'] - result = GPFacLiv < 1. + GPFacLiv = self.bilt["GPFacLiv"] + result = GPFacLiv < 1.0 messages = { True: f"GPFacLiv={GPFacLiv:.5f} : The Mortality-Adjusted Growth Patience Factor satisfies the Mortality-Adjusted Growth Impatience Condition (GICLiv) ℒÞ/G < 1.", - False: f"GPFacLiv={GPFacLiv:.5f} : The Mortality-Adjusted Growth Patience Factor violates the Mortality-Adjusted Growth Impatience Condition (GICLiv) ℒÞ/G < 1." + False: f"GPFacLiv={GPFacLiv:.5f} : The Mortality-Adjusted Growth Patience Factor violates the Mortality-Adjusted Growth Impatience Condition (GICLiv) ℒÞ/G < 1.", } verbose = self.verbose if verbose is None else verbose self.log_condition_result(name, result, messages[result], verbose) - - + def check_FVAC(self, verbose=None): """ Evaluate and report on the Finite Value of Autarky condition in the presence of income risk. """ name = "FVAC" - VAFac = self.bilt['VAFac'] - result = VAFac < 1. + VAFac = self.bilt["VAFac"] + result = VAFac < 1.0 messages = { True: f"VAFac={VAFac:.5f} : The Risk-Modified Finite Value of Autarky Factor satisfies the Risk-Modified Finite Value of Autarky Condition β(G‖Ψ‖_(1-ρ))^(1-ρ) < 1.", - False: f"VAFac={VAFac:.5f} : The Risk-Modified Finite Value of Autarky Factor violates the Risk-Modified Finite Value of Autarky Condition β(G‖Ψ‖_(1-ρ))^(1-ρ) < 1." + False: f"VAFac={VAFac:.5f} : The Risk-Modified Finite Value of Autarky Factor violates the Risk-Modified Finite Value of Autarky Condition β(G‖Ψ‖_(1-ρ))^(1-ρ) < 1.", } verbose = self.verbose if verbose is None else verbose self.log_condition_result(name, result, messages[result], verbose) - - + def check_WRIC(self, verbose=None): """ Evaluate and report on the Weak Return Impatience Condition. """ name = "WRIC" - WRPFac = self.bilt['WRPFac'] - result = WRPFac < 1. + WRPFac = self.bilt["WRPFac"] + result = WRPFac < 1.0 messages = { True: f"WRPFac={WRPFac:.5f} : The Weak Return Patience Factor satisfies the Weak Return Impatience Condition (WRIC) ℘ Þ/R < 1.", - False: f"WRPFac={WRPFac:.5f} : The Weak Return Patience Factor violates the Weak Return Impatience Condition (WRIC) ℘ Þ/R < 1." - } + False: f"WRPFac={WRPFac:.5f} : The Weak Return Patience Factor violates the Weak Return Impatience Condition (WRIC) ℘ Þ/R < 1.", + } verbose = self.verbose if verbose is None else verbose self.log_condition_result(name, result, messages[result], verbose) - def check_conditions(self, verbose=None): """ @@ -3471,17 +3497,17 @@ def check_conditions(self, verbose=None): None """ self.conditions = {} - self.bilt['conditions_report'] = '' + self.bilt["conditions_report"] = "" self.degenerate = False verbose = self.verbose if verbose is None else verbose # This method only checks for the conditions for infinite horizon models # with a 1 period cycle. If these conditions are not met, we exit early. if self.cycles != 0 or self.T_cycle > 1: - trivial_message = 'No conditions report was produced because this functionality is only supported for infinite horizon models with a cycle length of 1.' + trivial_message = "No conditions report was produced because this functionality is only supported for infinite horizon models with a cycle length of 1." self.log_condition_result(None, None, trivial_message, verbose) if not self.quiet: - _log.info(self.bilt['conditions_report']) + _log.info(self.bilt["conditions_report"]) return # Calculate some useful quantities that will be used in the condition checks @@ -3501,80 +3527,79 @@ def check_conditions(self, verbose=None): PerfForesightConsumerType.check_FVAC(self, verbose) self.check_FVAC(verbose) self.check_FHWC(verbose) - + # Exit now if verbose output was not requested. if not verbose: if not self.quiet: - _log.info(self.bilt['conditions_report']) + _log.info(self.bilt["conditions_report"]) return - + # Report on the degeneracy of the consumption function solution - if self.conditions['WRIC'] and self.conditions['FVAC']: - degen_message = '\nBecause both the WRIC and FVAC are satisfied, the recursive solution to the infinite horizon problem represents a contraction mapping on the consumption function. Thus a non-degenerate solution exists.' + if self.conditions["WRIC"] and self.conditions["FVAC"]: + degen_message = "\nBecause both the WRIC and FVAC are satisfied, the recursive solution to the infinite horizon problem represents a contraction mapping on the consumption function. Thus a non-degenerate solution exists." degenerate = False - elif not self.conditions['WRIC']: - degen_message = '\nBecause the WRIC is violated, the consumer is so pathologically patient that they will never consume at all. Thus the solution will be degenerate at c(m) = 0 for all m.\n' + elif not self.conditions["WRIC"]: + degen_message = "\nBecause the WRIC is violated, the consumer is so pathologically patient that they will never consume at all. Thus the solution will be degenerate at c(m) = 0 for all m.\n" degenerate = True - elif not self.conditions['FVAC']: + elif not self.conditions["FVAC"]: degen_message = "\nBecause the FVAC is violated, the recursive solution to the infinite horizon problem might not be a contraction mapping, so the produced solution might not be valid. Proceed with caution." degenerate = False self.log_condition_result(None, None, degen_message, verbose) self.degenerate = degenerate - + # Stop here if the solution is degenerate if degenerate: if not self.quiet: - _log.info(self.bilt['conditions_report']) + _log.info(self.bilt["conditions_report"]) return - + # Report on the limiting behavior of the consumption function as m goes to infinity - if self.conditions['RIC']: - if self.conditions['FHWC']: - RIC_message = '\nBecause both the RIC and FHWC condition are satisfied, the consumption function will approach the linear perfect foresight solution as m becomes arbitrarily large.' + if self.conditions["RIC"]: + if self.conditions["FHWC"]: + RIC_message = "\nBecause both the RIC and FHWC condition are satisfied, the consumption function will approach the linear perfect foresight solution as m becomes arbitrarily large." else: - RIC_message = '\nBecause the RIC is satisfied but the FHWC is violated, the GIC is satisfied.' + RIC_message = "\nBecause the RIC is satisfied but the FHWC is violated, the GIC is satisfied." else: - RIC_message = '\nBecause the RIC is violated, the FHWC condition is also violated. The consumer is pathologically impatient but has infinite expected future earnings. Thus the consumption function will not approach any linear limit as m becomes arbitrarily large, and the MPC will asymptote to zero.' + RIC_message = "\nBecause the RIC is violated, the FHWC condition is also violated. The consumer is pathologically impatient but has infinite expected future earnings. Thus the consumption function will not approach any linear limit as m becomes arbitrarily large, and the MPC will asymptote to zero." self.log_condition_result(None, None, RIC_message, verbose) - + # Report on whether a pseudo-steady-state exists at the individual level - if self.conditions['GICRaw']: - GIC_message = '\nBecause the GICRaw is satisfied, there exists a pseudo-steady-state wealth ratio at which the level of wealth is expected to grow at the same rate as permanent income.' + if self.conditions["GICRaw"]: + GIC_message = "\nBecause the GICRaw is satisfied, there exists a pseudo-steady-state wealth ratio at which the level of wealth is expected to grow at the same rate as permanent income." else: - GIC_message = '\nBecause the GICRaw is violated, there might not exist a pseudo-steady-state wealth ratio at which the level of wealth is expected to grow at the same rate as permanent income.' + GIC_message = "\nBecause the GICRaw is violated, there might not exist a pseudo-steady-state wealth ratio at which the level of wealth is expected to grow at the same rate as permanent income." self.log_condition_result(None, None, GIC_message, verbose) - + # Report on whether a target wealth ratio exists at the individual level - if self.conditions['GICMod']: - GICMod_message = '\nBecause the GICMod is satisfied, expected growth of the ratio of market resources to permanent income is less than one as market resources become arbitrarily large. Hence the consumer has a target ratio of market resources to permanent income.' + if self.conditions["GICMod"]: + GICMod_message = "\nBecause the GICMod is satisfied, expected growth of the ratio of market resources to permanent income is less than one as market resources become arbitrarily large. Hence the consumer has a target ratio of market resources to permanent income." else: - GICMod_message = '\nBecause the GICMod is violated, expected growth of the ratio of market resources to permanent income exceeds one as market resources go to infinity. Hence the consumer might not have a target ratio of market resources to permanent income.' + GICMod_message = "\nBecause the GICMod is violated, expected growth of the ratio of market resources to permanent income exceeds one as market resources go to infinity. Hence the consumer might not have a target ratio of market resources to permanent income." self.log_condition_result(None, None, GICMod_message, verbose) - + # Report on whether a target level of wealth exists at the aggregate level - if self.conditions['GICLiv']: - GICLiv_message = '\nBecause the GICLiv is satisfied, a target ratio of aggregate market resources to aggregate permanent income exists.' + if self.conditions["GICLiv"]: + GICLiv_message = "\nBecause the GICLiv is satisfied, a target ratio of aggregate market resources to aggregate permanent income exists." else: - GICLiv_message = '\nBecause the GICLiv is violated, a target ratio of aggregate market resources to aggregate permanent income might not exist.' + GICLiv_message = "\nBecause the GICLiv is violated, a target ratio of aggregate market resources to aggregate permanent income might not exist." self.log_condition_result(None, None, GICLiv_message, verbose) - + # Report on whether invariant distributions exist - if self.conditions['GICSdl']: - GICSdl_message = '\nBecause the GICSdl is satisfied, there exist invariant distributions of permanent income-normalized variables.' + if self.conditions["GICSdl"]: + GICSdl_message = "\nBecause the GICSdl is satisfied, there exist invariant distributions of permanent income-normalized variables." else: - GICSdl_message = '\nBecause the GICSdl is violated, there do not exist invariant distributions of permanent income-normalized variables.' + GICSdl_message = "\nBecause the GICSdl is violated, there do not exist invariant distributions of permanent income-normalized variables." self.log_condition_result(None, None, GICSdl_message, verbose) - + # Report on whether blah blah - if self.conditions['GICHrm']: - GICHrm_message = '\nBecause the GICHrm is satisfied, there exists a target ratio of the individual market resources to permanent income, under the permanent-income-neutral measure.' + if self.conditions["GICHrm"]: + GICHrm_message = "\nBecause the GICHrm is satisfied, there exists a target ratio of the individual market resources to permanent income, under the permanent-income-neutral measure." else: - GICHrm_message = '\nBecause the GICHrm is violated, there does not exist a target ratio of the individual market resources to permanent income, under the permanent-income-neutral measure..' + GICHrm_message = "\nBecause the GICHrm is violated, there does not exist a target ratio of the individual market resources to permanent income, under the permanent-income-neutral measure.." self.log_condition_result(None, None, GICHrm_message, verbose) - + if not self.quiet: - _log.info(self.bilt['conditions_report']) - + _log.info(self.bilt["conditions_report"]) def calc_stable_points(self): """ @@ -3597,7 +3622,6 @@ def calc_stable_points(self): ) return - # = Functions for generating discrete income processes and # simulated income shocks = # ======================================================== @@ -3893,7 +3917,7 @@ def __init__( # kinked R is now compatible with linear cFunc and cubic cFunc "aXtraCount": 48, # ...so need lots of extra gridpoints to make up for it - } + }, ) del init_kinked_R["Rfree"] # get rid of constant interest factor @@ -4106,7 +4130,7 @@ def apply_flat_income_tax( age_max=death_age, adjust_infl_to=adjust_infl_to, **income_calib, - SabelhausSong=True + SabelhausSong=True, ) # Initial distribution of wealth and permanent income diff --git a/HARK/ConsumptionSaving/ConsPrefShockModel.py b/HARK/ConsumptionSaving/ConsPrefShockModel.py index d7b93909e..4dcb61992 100644 --- a/HARK/ConsumptionSaving/ConsPrefShockModel.py +++ b/HARK/ConsumptionSaving/ConsPrefShockModel.py @@ -37,7 +37,7 @@ "PrefShkStd": [0.30], # Standard deviation of utility shocks "aXtraCount": 48, "CubicBool": False, # pref shocks currently only compatible with linear cFunc - } + }, ) # Make a dictionary to specify a "kinky preference" consumer @@ -49,7 +49,7 @@ "PrefShkStd": [0.30], # Standard deviation of utility shocks "aXtraCount": 48, "CubicBool": False, # pref shocks currently only compatible with linear cFunc - } + }, ) init_kinky_pref["BoroCnstArt"] = None diff --git a/HARK/ConsumptionSaving/ConsRiskyContribModel.py b/HARK/ConsumptionSaving/ConsRiskyContribModel.py index dcf94c064..368aea69d 100644 --- a/HARK/ConsumptionSaving/ConsRiskyContribModel.py +++ b/HARK/ConsumptionSaving/ConsRiskyContribModel.py @@ -1033,7 +1033,7 @@ def solve_RiskyContrib_Cns( AdjustPrb, DiscreteShareBool, joint_dist_solver, - **unused_params + **unused_params, ): """ Solves the consumption stage of the agent's problem @@ -1504,7 +1504,7 @@ def solve_RiskyContrib_Sha( ShareGrid, DiscreteShareBool, vFuncBool, - **unused_params + **unused_params, ): """ Solves the income-contribution-share stag of the agent's problem diff --git a/HARK/ConsumptionSaving/tests/test_IndShockConsumerType.py b/HARK/ConsumptionSaving/tests/test_IndShockConsumerType.py index 3a61b2166..f1881a275 100644 --- a/HARK/ConsumptionSaving/tests/test_IndShockConsumerType.py +++ b/HARK/ConsumptionSaving/tests/test_IndShockConsumerType.py @@ -922,4 +922,4 @@ def test_calc_jacobian(self): self.assertAlmostEqual(CJAC_Perm.T[30][29], -0.06120, places=HARK_PRECISION) self.assertAlmostEqual(CJAC_Perm.T[30][30], 0.05307, places=HARK_PRECISION) - self.assertAlmostEqual(CJAC_Perm.T[30][31], 0.04674, places=HARK_PRECISION) \ No newline at end of file + self.assertAlmostEqual(CJAC_Perm.T[30][31], 0.04674, places=HARK_PRECISION) diff --git a/HARK/ConsumptionSaving/tests/test_SmallOpenEconomy.py b/HARK/ConsumptionSaving/tests/test_SmallOpenEconomy.py index f27ca7471..aee2d3860 100644 --- a/HARK/ConsumptionSaving/tests/test_SmallOpenEconomy.py +++ b/HARK/ConsumptionSaving/tests/test_SmallOpenEconomy.py @@ -29,7 +29,7 @@ def test_small_open(self): Rfree=1.03, wRte=1.0, KtoLnow=1.0, - **copy.copy(init_cobb_douglas) + **copy.copy(init_cobb_douglas), ) small_economy.act_T = 400 # Short simulation history diff --git a/HARK/core.py b/HARK/core.py index d7db51b30..c2ec0ebd1 100644 --- a/HARK/core.py +++ b/HARK/core.py @@ -6,6 +6,8 @@ model adds an additional layer, endogenizing some of the inputs to the micro problem by finding a general equilibrium dynamic rule. """ +# Set logging and define basic functions +import logging import sys from collections import defaultdict, namedtuple from copy import copy, deepcopy @@ -27,8 +29,6 @@ from HARK.parallel import multi_thread_commands, multi_thread_commands_fake from HARK.utilities import NullFunc, get_arg_names -# Set logging and define basic functions -import logging logging.basicConfig(format="%(message)s") _log = logging.getLogger("HARK") _log.setLevel(logging.ERROR) diff --git a/HARK/mat_methods.py b/HARK/mat_methods.py index abe2cb4fd..2e3abbe6a 100644 --- a/HARK/mat_methods.py +++ b/HARK/mat_methods.py @@ -1,6 +1,7 @@ +from typing import List + import numpy as np from numba import njit -from typing import List @njit diff --git a/HARK/model.py b/HARK/model.py index 2ea919ea6..b03734338 100644 --- a/HARK/model.py +++ b/HARK/model.py @@ -2,6 +2,7 @@ Tools for crafting models. """ + class Control: """ Should go in different model support module. diff --git a/HARK/models/fisher.py b/HARK/models/fisher.py index 3f9730531..bc5aa83fd 100644 --- a/HARK/models/fisher.py +++ b/HARK/models/fisher.py @@ -8,23 +8,21 @@ # This way of distributing parameters across the scope is clunky # Can be handled better if parsed from a YAML file, probably # But it would be better to have a more graceful Python version as well. -CRRA = 2.0, +CRRA = (2.0,) model = { - 'shocks' : {}, - 'parameters' : { - 'DiscFac' : 0.96, - 'CRRA' : CRRA, - 'Rfree' : 1.03, - 'y' : [1.0, 1.0], - 'BoroCnstArt' : None, + "shocks": {}, + "parameters": { + "DiscFac": 0.96, + "CRRA": CRRA, + "Rfree": 1.03, + "y": [1.0, 1.0], + "BoroCnstArt": None, }, - 'dynamics' : { - 'm' : lambda Rfree, a, y : Rfree * a + y, - 'c' : Control(['m']), - 'a' : lambda m, c : m - c + "dynamics": { + "m": lambda Rfree, a, y: Rfree * a + y, + "c": Control(["m"]), + "a": lambda m, c: m - c, }, - 'reward' : { - 'u' : lambda c : c ** (1 - CRRA) / (1 - CRRA) - } -} \ No newline at end of file + "reward": {"u": lambda c: c ** (1 - CRRA) / (1 - CRRA)}, +} diff --git a/HARK/models/perfect_foresight.py b/HARK/models/perfect_foresight.py index cca33e2ed..4c3ceb3e3 100644 --- a/HARK/models/perfect_foresight.py +++ b/HARK/models/perfect_foresight.py @@ -4,29 +4,27 @@ # This way of distributing parameters across the scope is clunky # Can be handled better if parsed from a YAML file, probably # But it would be better to have a more graceful Python version as well. -CRRA = 2.0, +CRRA = (2.0,) LivPrb = 0.98 model = { - 'shocks' : { - 'live' : Bernoulli(p=LivPrb), + "shocks": { + "live": Bernoulli(p=LivPrb), }, - 'parameters' : { - 'DiscFac' : 0.96, - 'CRRA' : CRRA, - 'Rfree' : 1.03, - 'LivPrb' : LivPrb, - 'PermGroFac' : 1.01, - 'BoroCnstArt' : None, + "parameters": { + "DiscFac": 0.96, + "CRRA": CRRA, + "Rfree": 1.03, + "LivPrb": LivPrb, + "PermGroFac": 1.01, + "BoroCnstArt": None, }, - 'dynamics' : { - 'm' : lambda Rfree, a, y : Rfree * a + y, - 'c' : Control(['m']), - 'y' : lambda p : p, - 'p' : lambda PermGroFac, p: PermGroFac * p, - 'a' : lambda m, c : m - c + "dynamics": { + "m": lambda Rfree, a, y: Rfree * a + y, + "c": Control(["m"]), + "y": lambda p: p, + "p": lambda PermGroFac, p: PermGroFac * p, + "a": lambda m, c: m - c, }, - 'reward' : { - 'u' : lambda c : c ** (1 - CRRA) / (1 - CRRA) - } -} \ No newline at end of file + "reward": {"u": lambda c: c ** (1 - CRRA) / (1 - CRRA)}, +} diff --git a/HARK/tests/test_mat_methods.py b/HARK/tests/test_mat_methods.py index 7e5b84374..834d78b83 100644 --- a/HARK/tests/test_mat_methods.py +++ b/HARK/tests/test_mat_methods.py @@ -1,7 +1,9 @@ import unittest + import numpy as np -from HARK.utilities import jump_to_grid_1D, jump_to_grid_2D + from HARK.mat_methods import mass_to_grid +from HARK.utilities import jump_to_grid_1D, jump_to_grid_2D # Compare general mass_to_grid with jump_to_grid_1D diff --git a/examples/Calibration/Income_calibrations.ipynb b/examples/Calibration/Income_calibrations.ipynb index 9703b284a..719948309 100644 --- a/examples/Calibration/Income_calibrations.ipynb +++ b/examples/Calibration/Income_calibrations.ipynb @@ -96,7 +96,6 @@ } ], "source": [ - "\n", "age_min = 25\n", "age_max = 91\n", "# Cagetti has a year trend in his specification, so we have to say on what\n", @@ -114,7 +113,7 @@ " age_max=age_max,\n", " adjust_infl_to=adjust_infl_to,\n", " start_year=start_year,\n", - " **spec[1]\n", + " **spec[1],\n", " )\n", " MeanY = find_profile(params[\"PermGroFac\"], params[\"P0\"])\n", "\n", diff --git a/examples/Calibration/Life_Cycle_example.ipynb b/examples/Calibration/Life_Cycle_example.ipynb index 28316463f..8d1e95df9 100644 --- a/examples/Calibration/Life_Cycle_example.ipynb +++ b/examples/Calibration/Life_Cycle_example.ipynb @@ -47,7 +47,7 @@ " age_max=death_age,\n", " adjust_infl_to=adjust_infl_to,\n", " **income_calib[education],\n", - " SabelhausSong=True\n", + " SabelhausSong=True,\n", ")\n", "\n", "# Initial distribution of wealth and permanent income\n", diff --git a/examples/Calibration/SCF_distributions.ipynb b/examples/Calibration/SCF_distributions.ipynb index 16a8142e5..83769032c 100644 --- a/examples/Calibration/SCF_distributions.ipynb +++ b/examples/Calibration/SCF_distributions.ipynb @@ -84,7 +84,6 @@ } ], "source": [ - "\n", "# Formatting\n", "frame = frame.melt(id_vars=[\"base_year\", \"age\", \"education\", \"wave\"])\n", "aux = frame[\"variable\"].str.split(\"(Mean|Std)\", n=1, expand=True)\n", diff --git a/examples/Calibration/Sabelhaus_Song_var_profiles.ipynb b/examples/Calibration/Sabelhaus_Song_var_profiles.ipynb index 103baac3c..f005f7028 100644 --- a/examples/Calibration/Sabelhaus_Song_var_profiles.ipynb +++ b/examples/Calibration/Sabelhaus_Song_var_profiles.ipynb @@ -77,7 +77,6 @@ } ], "source": [ - "\n", "# Plot transitory shock variances\n", "plt.figure()\n", "for i in range(len(cohorts)):\n", diff --git a/examples/Calibration/US_SSA_life_tables.ipynb b/examples/Calibration/US_SSA_life_tables.ipynb index 6b76077c0..3f941ac35 100644 --- a/examples/Calibration/US_SSA_life_tables.ipynb +++ b/examples/Calibration/US_SSA_life_tables.ipynb @@ -59,7 +59,6 @@ } ], "source": [ - "\n", "tables = get_ssa_life_tables()\n", "print(tables.head)" ] @@ -73,7 +72,6 @@ }, "outputs": [], "source": [ - "\n", "# We will find 1-year survival probabilities from ages 21 to 100\n", "min_age = 21\n", "max_age = 100\n", @@ -111,7 +109,6 @@ } ], "source": [ - "\n", "# First, the \"longitudinal method\", which gives us the probabilities\n", "# experienced by agents born in \"year\" throughout their lived\n", "plt.figure()\n", @@ -156,7 +153,6 @@ } ], "source": [ - "\n", "# Second, the \"cross-sectional method\", which gives us the probabilities of\n", "# survivals of individuals of differnet ages that are alive in the given year.\n", "plt.figure()\n", diff --git a/examples/ConsIndShockModel/Finite Cyclical Test.ipynb b/examples/ConsIndShockModel/Finite Cyclical Test.ipynb index 6d05f9056..829647c62 100644 --- a/examples/ConsIndShockModel/Finite Cyclical Test.ipynb +++ b/examples/ConsIndShockModel/Finite Cyclical Test.ipynb @@ -8,9 +8,7 @@ "source": [ "# Initial imports and notebook setup, click arrow to show\n", "from HARK.ConsumptionSaving.ConsIndShockModel import IndShockConsumerType\n", - "from HARK.utilities import plot_funcs_der, plot_funcs\n", - "import matplotlib.pyplot as plt\n", - "import numpy as np\n", + "from HARK.utilities import plot_funcs\n", "\n", "mystr = lambda number: \"{:.4f}\".format(number)" ] diff --git a/examples/ConsIndShockModel/IndShockConsumerType_Jacobian_Example.ipynb b/examples/ConsIndShockModel/IndShockConsumerType_Jacobian_Example.ipynb index 0fc2cbcd6..cc0df25a2 100644 --- a/examples/ConsIndShockModel/IndShockConsumerType_Jacobian_Example.ipynb +++ b/examples/ConsIndShockModel/IndShockConsumerType_Jacobian_Example.ipynb @@ -30,9 +30,7 @@ "\n", "\n", "import time\n", - "import numpy as np\n", - "import matplotlib.pyplot as plt\n", - "from copy import copy, deepcopy" + "import matplotlib.pyplot as plt" ] }, { @@ -73,7 +71,6 @@ }, "outputs": [], "source": [ - "\n", "Agent = IndShockConsumerType(**Dict)" ] }, @@ -118,7 +115,6 @@ } ], "source": [ - "\n", "start = time.time()\n", "Agent.compute_steady_state()\n", "print(\"Seconds to compute steady state\", time.time() - start)" @@ -159,7 +155,6 @@ } ], "source": [ - "\n", "start = time.time()\n", "\n", "CJAC_Perm, AJAC_Perm = Agent.calc_jacobian(\"PermShkStd\", 300)\n", @@ -195,7 +190,6 @@ } ], "source": [ - "\n", "plt.plot(CJAC_Perm.T[0])\n", "plt.plot(CJAC_Perm.T[10])\n", "plt.plot(CJAC_Perm.T[30])\n", @@ -230,7 +224,6 @@ } ], "source": [ - "\n", "plt.plot(AJAC_Perm.T[0])\n", "plt.plot(AJAC_Perm.T[10])\n", "plt.plot(AJAC_Perm.T[30])\n", @@ -286,7 +279,6 @@ } ], "source": [ - "\n", "plt.plot(CJAC_Rfree.T[0])\n", "plt.plot(CJAC_Rfree.T[10])\n", "plt.plot(CJAC_Rfree.T[30])\n", @@ -320,7 +312,6 @@ } ], "source": [ - "\n", "plt.plot(AJAC_Rfree.T[0])\n", "plt.plot(AJAC_Rfree.T[10])\n", "plt.plot(AJAC_Rfree.T[30])\n", diff --git a/examples/ConsIndShockModel/IndShockConsumerType_Transition_Matrix_Example.ipynb b/examples/ConsIndShockModel/IndShockConsumerType_Transition_Matrix_Example.ipynb index d21aaa77d..22c1df453 100644 --- a/examples/ConsIndShockModel/IndShockConsumerType_Transition_Matrix_Example.ipynb +++ b/examples/ConsIndShockModel/IndShockConsumerType_Transition_Matrix_Example.ipynb @@ -47,16 +47,13 @@ }, "outputs": [], "source": [ - "\n", "from HARK.ConsumptionSaving.ConsIndShockModel import IndShockConsumerType\n", "\n", "\n", "import time\n", - "from copy import copy, deepcopy\n", + "from copy import deepcopy\n", "import numpy as np\n", - "import matplotlib.pyplot as plt\n", - "\n", - "import time" + "import matplotlib.pyplot as plt" ] }, { @@ -85,7 +82,6 @@ } ], "source": [ - "\n", "Dict = {\n", " # Parameters shared with the perfect foresight model\n", " \"CRRA\": 2, # Coefficient of relative risk aversion\n", @@ -150,75 +146,20 @@ "name": "stderr", "output_type": "stream", "text": [ - "GPFRaw = 0.992274 \n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "GPFNrm = 0.995482 \n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "GPFAggLivPrb = 0.986072 \n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Thorn = APF = 0.992274 \n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "PermGroFacAdj = 0.996777 \n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "uInvEpShkuInv = 0.996777 \n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "VAF = 0.965783 \n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "WRPF = 0.000000 \n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DiscFacGPFNrmMax = 0.983869 \n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ + "GPFRaw = 0.992274 \n", + "GPFNrm = 0.995482 \n", + "GPFAggLivPrb = 0.986072 \n", + "Thorn = APF = 0.992274 \n", + "PermGroFacAdj = 0.996777 \n", + "uInvEpShkuInv = 0.996777 \n", + "VAF = 0.965783 \n", + "WRPF = 0.000000 \n", + "DiscFacGPFNrmMax = 0.983869 \n", "DiscFacGPFAggLivPrbMax = 0.996471 \n" ] } ], "source": [ - "\n", "example1 = IndShockConsumerType(**Dict)\n", "example1.cycles = 0\n", "example1.solve()" @@ -257,7 +198,6 @@ }, "outputs": [], "source": [ - "\n", "# Simulation Parameters\n", "\n", "# Simulate\n", @@ -300,7 +240,6 @@ } ], "source": [ - "\n", "example1.define_distribution_grid(num_pointsP=110, timestonest=3)\n", "p = example1.dist_pGrid # Grid of permanent income levels\n", "\n", @@ -311,9 +250,7 @@ "asset = example1.aPol_Grid # Normalized Asset Policy Grid\n", "\n", "example1.calc_ergodic_dist()\n", - "vecDstn = (\n", - " example1.vec_erg_dstn\n", - ") # Distribution of market resources and permanent income as a vector (m*p)x1 vector where\n", + "vecDstn = example1.vec_erg_dstn # Distribution of market resources and permanent income as a vector (m*p)x1 vector where\n", "# m is the number of market resource gridpoints and p is the number of permanent income gridpoints\n", "erg_dstn = example1.erg_dstn\n", "\n", @@ -332,7 +269,6 @@ }, "outputs": [], "source": [ - "\n", "# Compute Aggregate Consumption and Aggregate Assets\n", "gridc = np.zeros((len(c), len(p)))\n", "grida = np.zeros((len(asset), len(p)))\n", @@ -373,7 +309,6 @@ } ], "source": [ - "\n", "print(\"TranMatrix Assets = \" + str(AggA))\n", "print(\"Simulated Assets = \" + str(Monte_Carlo_Assets))\n", "\n", @@ -400,8 +335,6 @@ }, "outputs": [], "source": [ - "\n", - "\n", "aLvls = [] # Time series of aggregate assets\n", "\n", "for i in range(example1.T_sim):\n", @@ -489,7 +422,6 @@ } ], "source": [ - "\n", "num_pts = len(example1.dist_mGrid)\n", "mdstn = np.zeros(num_pts)\n", "\n", @@ -548,7 +480,6 @@ } ], "source": [ - "\n", "dstn = example1.erg_dstn\n", "\n", "pdstn = np.zeros(len(dstn[0]))\n", @@ -686,7 +617,6 @@ } ], "source": [ - "\n", "mLvl = (\n", " example1.state_now[\"mNrm\"] * example1.state_now[\"pLvl\"]\n", ") # market resources from Monte Carlo Simulations\n", @@ -742,7 +672,6 @@ } ], "source": [ - "\n", "asset_Lvl = example1.state_now[\"aLvl\"] # market resources from Monte Carlo Simulations\n", "pmf = jump_to_grid_fast(\n", " aLvl_vals, vecDstn, example1.aPol_Grid\n", @@ -803,75 +732,20 @@ "name": "stderr", "output_type": "stream", "text": [ - "GPFRaw = 0.992274 \n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "GPFNrm = 0.995482 \n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "GPFAggLivPrb = 0.986072 \n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Thorn = APF = 0.992274 \n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "PermGroFacAdj = 0.996777 \n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "uInvEpShkuInv = 0.996777 \n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "VAF = 0.965783 \n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "WRPF = 0.000000 \n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DiscFacGPFNrmMax = 0.983869 \n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ + "GPFRaw = 0.992274 \n", + "GPFNrm = 0.995482 \n", + "GPFAggLivPrb = 0.986072 \n", + "Thorn = APF = 0.992274 \n", + "PermGroFacAdj = 0.996777 \n", + "uInvEpShkuInv = 0.996777 \n", + "VAF = 0.965783 \n", + "WRPF = 0.000000 \n", + "DiscFacGPFNrmMax = 0.983869 \n", "DiscFacGPFAggLivPrbMax = 0.996471 \n" ] } ], "source": [ - "\n", "ss = IndShockConsumerType(**Dict)\n", "ss.cycles = 0\n", "ss.solve()" @@ -899,7 +773,6 @@ }, "outputs": [], "source": [ - "\n", "# Change the income process to use Neutral Measure\n", "ss.neutral_measure = True\n", "ss.update_income_process()\n", @@ -925,7 +798,6 @@ } ], "source": [ - "\n", "# Set up grid and calculate steady state transition Matrices\n", "\n", "start = time.time()\n", @@ -937,9 +809,7 @@ "a = ss.aPol_Grid # Normalized Asset Policy grid\n", "\n", "ss.calc_ergodic_dist() # Calculate steady state distribution\n", - "vecDstn_fast = (\n", - " ss.vec_erg_dstn\n", - ") # Distribution as a vector (mx1) where m is the number of gridpoint on the market resources grid\n", + "vecDstn_fast = ss.vec_erg_dstn # Distribution as a vector (mx1) where m is the number of gridpoint on the market resources grid\n", "\n", "print(\n", " \"Seconds to calculate both the transition matrix and the steady state distribution with Harmenberg\",\n", @@ -978,8 +848,6 @@ } ], "source": [ - "\n", - "\n", "plt.plot(\n", " aLvls[100:], label=\"Monte Carlo\", linewidth=2.0\n", ") # Plot time series path of aggregate assets using Monte Carlo simulation methods\n", @@ -1024,9 +892,7 @@ " ss.calc_transition_matrix()\n", "\n", " ss.calc_ergodic_dist() # Calculate steady state distribution\n", - " vecDstn_fast = (\n", - " ss.vec_erg_dstn\n", - " ) # Distribution as a vector (mx1) where m is the number of gridpoint on the market resources grid\n", + " vecDstn_fast = ss.vec_erg_dstn # Distribution as a vector (mx1) where m is the number of gridpoint on the market resources grid\n", " Asset_val = np.dot(ss.aPol_Grid, vecDstn_fast)\n", "\n", " Agg_AVals.append(Asset_val)" @@ -1104,7 +970,6 @@ } ], "source": [ - "\n", "ss.AgentCount = 25000\n", "ss.T_sim = 700\n", "ss.initialize_sim()\n", @@ -1130,7 +995,6 @@ }, "outputs": [], "source": [ - "\n", "# We will solve a finite horizon problem that begins at the steady state computed above.\n", "# Therefore parameters must be specified as lists, each item's index indicating the period of the horizon.\n", "\n", @@ -1182,7 +1046,6 @@ }, "outputs": [], "source": [ - "\n", "dx = -0.05 # Change in the Interest Rate\n", "i = 10 # Period in which the change in the interest rate occurs\n", "\n", @@ -1210,7 +1073,6 @@ }, "outputs": [], "source": [ - "\n", "FinHorizonAgent.solve()" ] }, @@ -1239,7 +1101,6 @@ } ], "source": [ - "\n", "# Simulate with Monte Carlo\n", "\n", "FinHorizonAgent.PerfMITShk = True\n", @@ -1309,7 +1170,6 @@ } ], "source": [ - "\n", "# Change Income Process to allow permanent income shocks to be drawn from neutral measure\n", "FinHorizonAgent.mCount = ss.mCount\n", "FinHorizonAgent.mMax = ss.mMax\n", @@ -1394,7 +1254,6 @@ } ], "source": [ - "\n", "# plt.plot(AggC, label = 'without Harmenberg') #Without Neutral Measure\n", "plt.plot(\n", " AggC_fast, label=\" Transition Matrices\", linewidth=3.0\n", diff --git a/examples/ConsPortfolioModel/example_ConsPortfolioModel.ipynb b/examples/ConsPortfolioModel/example_ConsPortfolioModel.ipynb index bd7f6f4de..96b599de9 100644 --- a/examples/ConsPortfolioModel/example_ConsPortfolioModel.ipynb +++ b/examples/ConsPortfolioModel/example_ConsPortfolioModel.ipynb @@ -21,7 +21,7 @@ }, "outputs": [], "source": [ - "from copy import copy, deepcopy\n", + "from copy import copy\n", "from time import time\n", "\n", "import matplotlib.pyplot as plt\n", @@ -36,7 +36,6 @@ ")\n", "from HARK.ConsumptionSaving.ConsPortfolioModel import (\n", " PortfolioConsumerType,\n", - " init_portfolio,\n", ")\n", "from HARK.utilities import plot_funcs" ] diff --git a/examples/ConsPortfolioModel/example_ConsSequentialPortfolioModel.ipynb b/examples/ConsPortfolioModel/example_ConsSequentialPortfolioModel.ipynb index a54dd20a3..b2608a17c 100644 --- a/examples/ConsPortfolioModel/example_ConsSequentialPortfolioModel.ipynb +++ b/examples/ConsPortfolioModel/example_ConsSequentialPortfolioModel.ipynb @@ -9,13 +9,11 @@ "\"\"\"\n", "Example implementations of SequentialPortfolioConsumerType\n", "\"\"\"\n", - "from copy import copy\n", "from time import time\n", "\n", "import matplotlib.pyplot as plt\n", "import numpy as np\n", "\n", - "from HARK.ConsumptionSaving.ConsIndShockModel import init_lifecycle\n", "from HARK.ConsumptionSaving.ConsPortfolioModel import (\n", " SequentialPortfolioConsumerType,\n", " init_portfolio,\n", diff --git a/examples/ConsumptionSaving/example_ConsRiskyContribModel.ipynb b/examples/ConsumptionSaving/example_ConsRiskyContribModel.ipynb index 84fa55e92..a5b8d42ba 100644 --- a/examples/ConsumptionSaving/example_ConsRiskyContribModel.ipynb +++ b/examples/ConsumptionSaving/example_ConsRiskyContribModel.ipynb @@ -27,12 +27,9 @@ }, "outputs": [], "source": [ - "\n", - "\n", "def plot_slices_3d(\n", " functions, bot_x, top_x, y_slices, N=300, y_name=None, titles=None, ax_labs=None\n", "):\n", - "\n", " import matplotlib.pyplot as plt\n", "\n", " if type(functions) == list:\n", @@ -52,7 +49,6 @@ " ax = fig.add_subplot(1, nfunc, k + 1)\n", "\n", " for y in y_slices:\n", - "\n", " if y_name is None:\n", " lab = \"\"\n", " else:\n", @@ -89,7 +85,6 @@ " titles=None,\n", " ax_labs=None,\n", "):\n", - "\n", " import matplotlib.pyplot as plt\n", "\n", " if type(functions) == list:\n", @@ -113,7 +108,6 @@ " ax = fig.add_subplot(nws, nfunc, j * nfunc + k + 1)\n", "\n", " for y in y_slices:\n", - "\n", " if slice_names is None:\n", " lab = \"\"\n", " else:\n", @@ -244,9 +238,9 @@ "\n", "# Adjust discounting and returns distribution so that they make sense in a\n", "# 4-period model\n", - "par_finite[\"DiscFac\"] = 0.95 ** 15\n", - "par_finite[\"Rfree\"] = 1.03 ** 15\n", - "par_finite[\"RiskyAvg\"] = 1.08 ** 15 # Average return of the risky asset\n", + "par_finite[\"DiscFac\"] = 0.95**15\n", + "par_finite[\"Rfree\"] = 1.03**15\n", + "par_finite[\"RiskyAvg\"] = 1.08**15 # Average return of the risky asset\n", "par_finite[\"RiskyStd\"] = 0.20 * np.sqrt(15) # Standard deviation of (log) risky returns\n", "\n", "\n", @@ -335,7 +329,6 @@ }, "outputs": [], "source": [ - "\n", "import pandas as pd\n", "\n", "df = contrib_agent.history\n", diff --git a/examples/ConsumptionSaving/example_TractableBufferStockModel.ipynb b/examples/ConsumptionSaving/example_TractableBufferStockModel.ipynb index 68ee63b8e..7fbbdcfd0 100644 --- a/examples/ConsumptionSaving/example_TractableBufferStockModel.ipynb +++ b/examples/ConsumptionSaving/example_TractableBufferStockModel.ipynb @@ -21,7 +21,6 @@ "from time import process_time # timing utility\n", "from HARK.distribution import DiscreteDistributionLabeled\n", "from HARK.ConsumptionSaving.TractableBufferStockModel import TractableConsumerType\n", - "import numpy as np\n", "\n", "do_simulation = True" ] diff --git a/examples/Distributions/ExpectedValue.ipynb b/examples/Distributions/ExpectedValue.ipynb index 62a519f08..6597ade76 100644 --- a/examples/Distributions/ExpectedValue.ipynb +++ b/examples/Distributions/ExpectedValue.ipynb @@ -17,7 +17,9 @@ { "cell_type": "code", "execution_count": 1, - "metadata": {}, + "metadata": { + "tags": [] + }, "outputs": [], "source": [ "from time import time\n", @@ -41,7 +43,9 @@ { "cell_type": "code", "execution_count": 2, - "metadata": {}, + "metadata": { + "tags": [] + }, "outputs": [], "source": [ "dd_0_1_20 = Normal().discretize(20)\n", @@ -66,13 +70,15 @@ { "cell_type": "code", "execution_count": 3, - "metadata": {}, + "metadata": { + "tags": [] + }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "4.33 µs ± 18.3 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)\n" + "1.63 µs ± 2.66 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)\n" ] } ], @@ -93,13 +99,15 @@ { "cell_type": "code", "execution_count": 4, - "metadata": {}, + "metadata": { + "tags": [] + }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "202 µs ± 1.8 µs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)\n" + "81.4 µs ± 525 ns per loop (mean ± std. dev. of 7 runs, 10,000 loops each)\n" ] } ], @@ -127,13 +135,15 @@ { "cell_type": "code", "execution_count": 5, - "metadata": {}, + "metadata": { + "tags": [] + }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "9.86 µs ± 82.3 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)\n" + "3.69 µs ± 51 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)\n" ] } ], @@ -153,13 +163,15 @@ { "cell_type": "code", "execution_count": 6, - "metadata": {}, + "metadata": { + "tags": [] + }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "208 µs ± 10.2 µs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)\n" + "88.2 µs ± 1.16 µs per loop (mean ± std. dev. of 7 runs, 10,000 loops each)\n" ] } ], @@ -186,13 +198,15 @@ { "cell_type": "code", "execution_count": 7, - "metadata": {}, + "metadata": { + "tags": [] + }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "28 µs ± 858 ns per loop (mean ± std. dev. of 7 runs, 10,000 loops each)\n" + "9.78 µs ± 60.2 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)\n" ] } ], @@ -205,13 +219,15 @@ { "cell_type": "code", "execution_count": 8, - "metadata": {}, + "metadata": { + "tags": [] + }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "515 µs ± 4.25 µs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)\n" + "214 µs ± 2.88 µs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)\n" ] } ], @@ -238,7 +254,9 @@ { "cell_type": "code", "execution_count": 9, - "metadata": {}, + "metadata": { + "tags": [] + }, "outputs": [], "source": [ "PermShkDstn = MeanOneLogNormal().discretize(200)\n", @@ -255,13 +273,15 @@ { "cell_type": "code", "execution_count": 10, - "metadata": {}, + "metadata": { + "tags": [] + }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "81.3 ms ± 7.25 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)\n" + "16.7 ms ± 499 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n" ] } ], @@ -274,13 +294,15 @@ { "cell_type": "code", "execution_count": 11, - "metadata": {}, + "metadata": { + "tags": [] + }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "582 ms ± 29.4 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n" + "246 ms ± 7.07 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n" ] } ], @@ -307,7 +329,9 @@ { "cell_type": "code", "execution_count": 12, - "metadata": {}, + "metadata": { + "tags": [] + }, "outputs": [], "source": [ "size = np.arange(1, 11) * 100\n", @@ -320,7 +344,6 @@ " TranShkDstn = MeanOneLogNormal().discretize(n)\n", " IncShkDstn = combine_indep_dstns(PermShkDstn, TranShkDstn)\n", "\n", - " m_next = lambda X, a, r: r * a / X[0] + X[1]\n", " a_grid = np.linspace(0, 20, 100).reshape((10, 10))\n", " R = 1.05\n", "\n", @@ -339,11 +362,13 @@ { "cell_type": "code", "execution_count": 13, - "metadata": {}, + "metadata": { + "tags": [] + }, "outputs": [ { "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAjwAAAHOCAYAAACcvdMVAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8pXeV/AAAACXBIWXMAAA9hAAAPYQGoP6dpAAByAUlEQVR4nO3dd3gU1dvG8e+mbBJSKQk19N47UgQUBBFRAQuISLEi/hC72LFhB9RXUFRUBFEQxAJSlCqCSBNEehVChxRC6p73jyVLliSQhCSTbO7Pde2VnbKzz85suTNz5ozNGGMQERER8WBeVhcgIiIikt8UeERERMTjKfCIiIiIx1PgEREREY+nwCMiIiIeT4FHREREPJ4Cj4iIiHg8BR4RERHxeAo8IiIi4vGKROAZPHgwVatWtbqMImXv3r3YbDY+//zzfHuOqlWrMnjw4HxbvuS9JUuWYLPZWLJkidWlXNSUKVOoW7cuvr6+hIWF5dvzHDlyhJtvvpnSpUtjs9kYN25cvj3X5SgK34FxcXHcfffdlCtXDpvNxsiRI3O9rLT36cyZM/OuwGx48cUXsdlsHD9+vECf93J17tyZzp07W11GoWdZ4LHZbNm6FcYv5i1btvDiiy+yd+9eq0spkg4dOsSLL77Ihg0brC6lUFq5ciUvvvgip0+fzvUyPvzww3wNu/lp69atDB48mBo1ajBp0iQ+/vjjfHuuhx9+mPnz5zNq1CimTJnCtddem2/PdSlF/XPx2muv8fnnnzNs2DCmTJnCwIEDrS6p0Jk2bVqhDdXFgY9VTzxlyhS34S+//JKFCxdmGF+vXj0mTZqEw+EoyPIuasuWLYwePZrOnTsX+v+6CqNDhw4xevRoqlatStOmTa0up9BZuXIlo0ePZvDgwbneu/Hhhx9SpkyZDHvgOnbsyNmzZ7Hb7ZdfaD5ZsmQJDoeD8ePHU7NmzXx9rt9++40bb7yRxx57LF+fJzsu9rkobN+Bmfntt9+44ooreOGFF6wupdCaNm0amzdvvqy9X5lZsGBBni7PU1kWeO644w634VWrVrFw4cIM40Uk73h5eeHv7291GRd19OhRgHw9lJX+uQrieS6Xr6+v1SVc0tGjR6lfv77VZRRLhfkfmELFFBLDhw83WZUzaNAgU6VKFdfwnj17DGDeeust88EHH5hq1aqZgIAAc80115j9+/cbh8NhXnrpJVOxYkXj7+9vbrjhBnPixIkMy507d67p0KGDKVGihAkKCjLXXXed2bx580XrnDx5sgEy3BYvXuya5//+7/9M/fr1jd1uN+XLlzcPPPCAOXXqVLbWw3///WeGDh1qypcvb+x2u6lataq5//77TWJiojHGmBMnTphHH33UNGzY0AQGBprg4GBz7bXXmg0bNrgtJ20dTZ482W38v//+a2655RZTpkwZ4+/vb2rXrm2efvrpLNd1mhdeeCHD9qlSpYoZNGiQazg7tS1evDjT9Ze+zlWrVpnu3bubkJAQExAQYDp27GhWrFiRrfWXkJBgnn/+eVOjRg1jt9tNpUqVzOOPP24SEhJc89x5553Gz8/PbNmyxe2x3bp1M2FhYebgwYPGmPPbeunSpebee+81pUqVMsHBwWbgwIHm5MmTGZ47u++ni22DtPV84W3Pnj3GGGM+++wzc9VVV5nw8HBjt9tNvXr1zIcffphhu1z4+E6dOrmt//TvV2OM+fbbb03z5s2Nv7+/KV26tBkwYID577//3OYZNGiQCQwMNP/995+58cYbTWBgoClTpox59NFHTUpKyqU3jrn0ZyOz2l944YUsl7dx40YzaNAgU61aNePn52fKli1rhgwZYo4fP37ROrL6HBuT+Xs9/WPStkVavT179jTLly83rVq1Mn5+fqZatWrmiy++yPD4U6dOmZEjR5oqVaoYu91uKlasaAYOHGiOHTt2yc9FZp/LuLg488gjj5hKlSoZu91uateubd566y3jcDjc5gPM8OHDzezZs02DBg2M3W439evXN/PmzbvoOkpz5MgRM3ToUBMREWH8/PxM48aNzeeff+6anlXt6dfThRYsWGDat29vQkNDTWBgoKldu7YZNWpUhmV+88035pVXXjEVK1Y0fn5+5uqrrzY7duzIsLzsvH+NufT3X9q2P3bsmGvc3r17TY0aNUyDBg3M4cOHjTHGbN++3fTp08eULVvW+Pn5mYoVK5rbbrvNnD59OsvX3KlTpwzrKP02vdR6vphOnTq5PuO5WX+rVq0yPXr0MGFhYaZEiRKmUaNGZty4cW7z/Prrr67vt9DQUHPDDTdk+A5NW3/btm0zAwYMMCEhIaZMmTLm2WefNQ6Hw+zfv9/ccMMNJjg42JQtW9a8/fbbGWrJznd4bhXpwNO0aVNTv3598+6775pnn33W2O12c8UVV5inn37atGvXzrz33ntmxIgRxmazmSFDhrgt88svvzQ2m81ce+215v333zdvvPGGqVq1qgkLC7voB3XXrl1mxIgRBjBPP/20mTJlipkyZYrrg5C2wbt27Wref/998+CDDxpvb2/TqlUrk5SUdNF1cPDgQVOhQgVTokQJM3LkSDNx4kTz3HPPmXr16rl+FNasWWNq1KhhnnrqKfPRRx+5gl1oaKjrhzr9OkofJDZu3GhCQkJM6dKlzahRo8xHH31knnjiCdOoUaMs13Wa7ASe7NR2+PBh89JLLxnA3Hvvva71t2vXLmOM80Nlt9tN27ZtzTvvvGPGjh1rGjdubOx2u1m9evVF119qaqrp1q2ba/199NFH5sEHHzQ+Pj7mxhtvdM136tQpU6lSJdOqVSvXD/XEiRMNYKZMmeKaL+0HrlGjRubKK6807733nhk+fLjx8vIyHTt2dPthye776VLbYOPGjaZ///4GMGPHjnWtn7i4OGOMMa1atTKDBw82Y8eONe+//77p1q2bAcwHH3zgeo7Zs2ebSpUqmbp167oev2DBAmNM5oEn7XW2atXKjB071jz11FMmICDAVK1a1S2MDBo0yPj7+5sGDRqYoUOHmgkTJpi+ffsaIEPoykx2PhuzZ882vXv3NoCZMGGCmTJlitm4cWOWy3z77bfNlVdeaV566SXz8ccfm4ceesgEBASY1q1bZ/jhT2/Xrl1mypQpBjDXXHONaz2lr/NCWQWeOnXqmLJly5qnn37afPDBB6Z58+bGZrO5hd3Y2FjTsGFD4+3tbe655x4zYcIE8/LLL5tWrVqZ9evXX/JzceHn0uFwmKuvvtrYbDZz9913mw8++MD06tXLAGbkyJFudQOmSZMmpnz58ubll18248aNM9WrVzclSpS4ZDCMj4839erVM76+vubhhx827733nrnyyisN4PpBPHz4sJkyZYopU6aMadq0aYb37IU2b95s7Ha7admypRk/fryZOHGieeyxx0zHjh1d86S9T5s1a2ZatGhhxo4da1588UVTokQJ07p160y3y6Xev9n5/rsw8OzcudNUrlzZNG3a1DUuMTHRVKtWzVSoUMG88sor5pNPPjGjR482rVq1Mnv37s1yXS5YsMA0bdrUlClTxrWOZs+ene31fDFZBZ7srL8FCxYYu91uqlSpYl544QUzYcIEM2LECNO1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x9ChQ3n55ZcpVaoUXl5ejBw5Mk9OW83qv8304SW/akub56233srydPULv3wufHyjRo149913M51+YRBev3696wO0adMmt/+ucqKg3k+7du2iS5cu1K1bl3fffZfIyEjsdjtz585l7NixBXLaclafLavceuutrFy5kscff5ymTZsSFBSEw+Hg2muvzfX6yOln4FLfN4VBYaoxICCAZcuWsXjxYn7++Wd++eUXvvnmG66++moWLFjgVqtVdfft25cvvviCqVOnct9992WY/s477zB48GDmzJnDggULGDFiBGPGjGHVqlVUqlQpX2vLCavWX2bPm51acvodnlOF95c9H6Xt2o2IiKBr1645fnxWX4hVqlQBYNu2bVSvXt01PikpiT179lz0ucLDwwkJCWHz5s0Xfe6ZM2dy1VVX8emnn7qNP336dIb0nF5aPZdafsmSJTPt8C47/2Fkt7as1l/adgkJCcnVdqlRowYbN26kS5culzxMcObMGYYMGUL9+vVp164db775Jr1796ZVq1YZ5t2xYwdXXXWVazguLo6oqCiuu+46t7ov9X7K7jbIqvYff/yRxMREfvjhB7f/oDLbzZvdwyTp37MX7qHatm2ba/rlupzPRlZOnTrFr7/+yujRo3n++edd49P2uOVW2n/Bp0+fdtvNn9v/ssH5Hsntds9MlSpVWLRoEbGxsW57ebZu3eqanheqVKnC33//jcPhcNv7cLnP4+XlRZcuXejSpQvvvvsur732Gs888wyLFy/O0Xshu+/f7H720rz11lv4+PjwwAMPEBwczO23355hnkaNGtGoUSOeffZZVq5cSfv27Zk4cSKvvPJKlsu92G9HfqznS0n77tq8eXOW6z39Or7Q1q1bKVOmjNvencutJ7vf4blRpNvw5Fb37t0JCQnhtddeIzk5OcP0Y8eOXfTxaRv3wmDQtWtX7HY77733nltq/fTTT4mOjqZnz55ZLtPLy4ubbrqJH3/8kb/++ivD9LTleXt7Z0jnM2bMcLVPykp4eDgdO3bks88+Y//+/ZkuG5xvuOjoaP7++2/XuKioKGbPnn3R5eektqzWX4sWLahRowZvv/02cXFxGZZ/qe1y6623cvDgQSZNmpRh2tmzZzlz5oxr+Mknn2T//v188cUXvPvuu1StWpVBgwaRmJiY4bEff/yx2/tkwoQJpKSk0KNHDyD776fsboOs1k/af0jp542Ojmby5MkZnjMwMDBbPTW3bNmSiIgIJk6c6Pba582bx7///nvR92xOXM5nIyuZrQ/gsnuyTfsRWLZsmWvcmTNn+OKLL3K9zL59+7Jx48ZMP0dp9We13TNz3XXXkZqaygcffOA2fuzYsdhsNtd783Jdd911HD58mG+++cY1LiUlhffff5+goCA6deqU42WePHkyw7i0PbqZff4uJrvv3+x+9tLYbDY+/vhjbr75ZgYNGsQPP/zgmhYTE0NKSorb/I0aNcLLy+uS9QcGBmZ6qDU/1nN2NG/enGrVqjFu3LgM77u09VK+fHmaNm3KF1984TbP5s2bWbBggesfv7yQk+/w/fv3uwJhdhXLPTwhISFMmDCBgQMH0rx5c/r160d4eDj79+/n559/pn379hm+SNJr2rQp3t7evPHGG0RHR+Pn58fVV19NREQEo0aNYvTo0Vx77bXccMMNbNu2jQ8//JBWrVpdslPF1157jQULFtCpUyfuvfde6tWrR1RUFDNmzGDFihWEhYVx/fXX89JLLzFkyBDatWvHpk2bmDp1qtt/zVl577336NChA82bN+fee++lWrVq7N27l59//tnVnX2/fv148skn6d27NyNGjCA+Pp4JEyZQu3btSzYCzW5tNWrUICwsjIkTJxIcHExgYCBt2rShWrVqfPLJJ/To0YMGDRowZMgQKlasyMGDB1m8eDEhISH8+OOPWT7/wIED+fbbb7n//vtZvHgx7du3JzU1la1bt/Ltt98yf/58WrZsyW+//caHH37ICy+8QPPmzQGYPHkynTt35rnnnuPNN990W25SUhJdunTh1ltvdW3PDh06cMMNNwA5ez9lZxu0aNECgGeeeYZ+/frh6+tLr1696NatG3a7nV69enHfffcRFxfHpEmTiIiIICoqyq3mFi1aMGHCBF555RVq1qxJREREpm2MfH19eeONNxgyZAidOnWif//+HDlyhPHjx1O1alUefvjhi27z7AoPD7+sz0ZmQkJC6NixI2+++SbJyclUrFiRBQsWsGfPnsuqtVu3blSuXJm77rqLxx9/HG9vbz777DPXNs2Nxx9/nJkzZ3LLLbcwdOhQWrRowcmTJ/nhhx+YOHEiTZo0uejn4kK9evXiqquu4plnnmHv3r00adKEBQsWMGfOHEaOHOnWQPly3HvvvXz00UcMHjyYtWvXUrVqVWbOnMnvv//OuHHjMrQhyo6XXnqJZcuW0bNnT6pUqcLRo0f58MMPqVSpEh06dMjRsnLy/s3OZy89Ly8vvvrqK2666SZuvfVW5s6dy9VXX81vv/3Ggw8+yC233ELt2rVJSUlhypQpeHt7X7KdVosWLfjmm2945JFHaNWqFUFBQfTq1Stf1nN2eHl5MWHCBHr16kXTpk0ZMmQI5cuXZ+vWrfzzzz/Mnz8fcO7x6tGjB23btuWuu+7i7NmzvP/++4SGhvLiiy/mWT3Z/Q4HuPPOO1m6dGnODs9d1jleeSi3HQ+ml3Yq3oWnC6adTnrh6d6LFy823bt3N6Ghocbf39/UqFHDDB482Pz111+XrHfSpEmmevXqxtvbO8Mp6h988IGpW7eu8fX1NWXLljXDhg3LdseD+/btM3feeacJDw83fn5+pnr16mb48OGuUwsTEhLMo48+asqXL28CAgJM+/btzR9//JHhtMSsOh7cvHmz6d27twkLCzP+/v6mTp065rnnnnObZ8GCBaZhw4bGbrebOnXqmK+++irbp6VnpzZjjJkzZ46pX7++8fHxyVDn+vXrTZ8+fUzp0qWNn5+fqVKlirn11lvNr7/+esn1l5SUZN544w3ToEED4+fnZ0qWLGlatGhhRo8ebaKjo01MTIypUqWKad68uVvXBcYY8/DDDxsvLy/zxx9/GGMydjxYsmRJExQUZAYMGJBpR5bZfT9lZxu8/PLLpmLFisbLy8vtVOgffvjBNG7c2Pj7+5uqVauaN954w3z22WcZTpc+fPiw6dmzpwkODjZw6Y4Hv/nmG9OsWTPj5+dnSpUqddGOBy+U1WncmcnOZyMnp6X/999/rnUZGhpqbrnlFnPo0CHDJTosTEMmp6UbY8zatWtNmzZtjN1uN5UrVzbvvvvuRTsevFBm7/kTJ06YBx980FSsWNHVodqgQYPc+sLJ6nORWXcRsbGx5uGHHzYVKlQwvr6+platWhftePBCF35+s3LkyBEzZMgQU6ZMGWO3202jRo0yPXU+u6el//rrr+bGG280FSpUMHa73VSoUMH079/fbN++3TVPVt/lWX2vZef9a8ylP3uZvffi4+NNp06dTFBQkFm1apXZvXu3GTp0qKlRo4bx9/c3pUqVMldddZVZtGjRJV97XFycuf32201YWJghk44Hs7OeM5PVaenZXX8rVqww11xzjQkODjaBgYGmcePGGbosWLRokWnfvr0JCAgwISEhplevXll2PHjhZzer745OnTqZBg0auI271Hd4+sfmNMLYjClELetECpHPP/+cIUOGsGbNmky7ChARkaKjWLbhERERkeJFgUdEREQ8ngKPiIiIeDy14RERERGPpz08IiIi4vEUeERERMTjFemOBx0OB4cOHSI4ODhfuqEWERGRvGeMITY2lgoVKmS4aGp+KdKB59ChQ5d9MTERERGxxoEDBwrsgqtFOvCkdbd94MABQkJCLK5GREREsiMmJobIyMh8u2xGZop04Ek7jBUSEqLAIyIiUsQUZHMUNVoWERERj6fAIyIiIh6vSB/Syq7U1FSSk5OtLkNyydfXF29vb6vLEBGRIsyjA48xhsOHD3P69GmrS5HLFBYWRrly5dT9gIiI5IpHB560sBMREUGJEiX0Y1kEGWOIj4/n6NGjAJQvX97iikREpCjy2MCTmprqCjulS5e2uhy5DAEBAQAcPXqUiIgIHd4SEZEc89hGy2ltdkqUKGFxJZIX0raj2mKJiEhueGzgSaPDWJ5B21FERC6HxwceEREREQUeERER8XgKPEVE586dGTlyJABVq1Zl3LhxltaTleeee457773XNWyM4d5776VUqVLYbDY2bNhAv379eOeddyysUkREihuPPUvLk61Zs4bAwMBszVu1alVGjhzpCkv56fDhw4wfP55Nmza5xv3yyy98/vnnLFmyhOrVq1OmTBmeffZZOnbsyN13301oaGi+1yUiIrmUkgQ+dquryBPaw1MEhYeHF8qzzz755BPatWtHlSpVXON27dpF+fLladeuHeXKlcPHx4eGDRtSo0YNvvrqKwurFRGRizqxC95vAVvnWl1JnihWgccYQ3xSSoHfjDE5qvPMmTPceeedBAUFUb58+QyHf9If0jLG8OKLL1K5cmX8/PyoUKECI0aMAJyHwfbt28fDDz+MzWa76JlOp0+f5u677yY8PJyQkBCuvvpqNm7cCMD27dux2Wxs3brV7TFjx46lRo0aruHp06fTq1cv1/DgwYP53//+x/79+7HZbFStWtU1rVevXkyfPj1H60VERApIYhx8cwdE74ffx4HDYXVFl61YHdI6m5xK/efnF/jzbnmpOyXs2V/Vjz/+OEuXLmXOnDlERETw9NNPs27dOpo2bZph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Zs2asXXrVrcerOvVq8fff/9NQkKCa9yqVasuWU9kZCT3338/s2bN4tFHH2XSpEmA84wugNTU1Fy/1gtt3ryZZs2a5dnyRESKjV9Hw56l4BvobKQcEGZ1RYWOAk8h4+/vz5NPPskTTzzBl19+ya5du1i1ahWffvop4Dx9u0yZMtx4440sX76cPXv2sGTJEkaMGMF///0HwFVXXUVcXJzbKd633347NpuNe+65hy1btjB37lzefvvti9YycuRI5s+fz549e1i3bh2LFy+mXr16AFSpUgWbzcZPP/3EsWPHiIuLu+zXvnz5crp1K76dYomI5MrmWbDyPef9m/4Pyta3tp5CSoGnEHruued49NFHef7556lXrx633XYbR48eBaBEiRIsW7aMypUr06dPH+rVq8ddd91FQkICISHO0w5Lly5N79693Q57BQUF8eOPP7Jp0yaaNWvGM888wxtvvHHROlJTUxk+fDj16tXj2muvpXbt2nz44YcAVKxYkdGjR/PUU09RtmxZHnzwQQA+//zzXPXj88cffxAdHc3NN9+c48eKiBRbR/6BOcOd99s/BA16W1tPIWYzRfjKnTExMYSGhhIdHe36sU+TkJDAnj17qFatWoYGvMXB33//zTXXXMOuXbsICiq4i8S98MILLF26lCVLluTocbfddhtNmjTh6aefznR6cd+eIiIZnD0FH18Fp/ZA9avgju/Ay9vqqrLlYr/f+UV7eDxU48aNeeONN9izZ0+BPu+8efN48803c/SYpKQkGjVqxMMPP5xPVYmIeBhHKnx3jzPshFWGmz8rMmHHKsXrLK1iJru9H+elP//8M8ePsdvtPPvss/lQjYiIh1oyBnYuBJ8AZyPlEqWsrqjQ0x4eERGRouTfn2DZW877N7wP5fOvfzRPosAjIiJSVBzbBrPvd96/4gFofIu19RQhHh94inCbbElH21FEir2EaJg+AJJioeqVcM1LVldUpHhs4PH19QXQpQo8RNp2TNuuIiLFisPh3LNzYgeEVISbJ4O3vg9zwmMbLXt7exMWFubWf01u+ocRaxljiI+P5+jRo4SFheHtrbMQRKQYWv42bJsL3n5w2xQI0mV4cspjAw/guhp3WuiRoissLCzD1dVFRIqF7fNh8WvO+9e/CxVbWFtPEeXRgcdms1G+fHkiIiJITk62uhzJJV9fX+3ZEZHi6cQu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8vmfeOIJvvvuO7744gvWrVtHzZo16d69OydPnmT8+PG89NJLVKpUiaioKFcQyswjjzzC77//zg8//MDChQtZvnw569atc5vniy++wG638/vvvzNx4sRMl/P444+zdOlS5syZw4IFC1iyZEmG5Xz++efYbLYsawHo168ftWvX5rXXXgPghRdeYNGiRcybN4/Q0FCMMQwePJirr76agQMHXnRZIlLAjIh4nLVr1xrA7N27N9PpnTp1Mg899FCG8WfPnjVt2rQx119/vUlNTTUJCQmmRIkSZuXKlW7z3XXXXaZ///6ZLjsuLs74+vqaqVOnusYlJSWZChUqmDfffNMYY8zYsWNNlSpVLvoaYmJijK+vr5kxY4Zr3OnTp02JEiVctXfq1Mk0a9bsoq8vNjbW2O128+2337qmnzhxwgQEBLitg1mzZpk6depctCZjjPnxxx+Nn5+feeWVV0zJkiXN5s2bXdOWL19ubDabadKkiev2999/X3KZIpL/1IZHxAM1adKELl260KhRI7p37063bt24+eabKVmy5EUfN3ToUGJjY1m4cCFeXl7s3LmT+Ph4rrnmGrf5kpKSaNasWabL2LVrF8nJybRv3941ztfXl9atW/Pvv/9m+zXs3r2b5ORkWrdu7RoXGhpKnTp13OZr0aLFRZeza9cukpKSaNOmjWtcqVKlMiynd+/e9O7d+5J1XX/99dSvX5+XXnqJBQsW0KBBA9e0Dh064HA4LrkMESl4CjwiHsjb25uFCxeycuVKFixYwPvvv88zzzzD6tWrqVatWqaPeeWVV5g/fz5//vknwcHBAMTFxQHw888/U7FiRbf5/fz88vdFZFNB93Hzyy+/sHXrVlJTUylbtmyBPreI5J7a8Ih4KJvNRvv27Rk9ejTr16/Hbrcze/bsTOf97rvveOmll/j222+pUaOGa3z9+vXx8/Nj//791KxZ0+2W1enWNWrUcLWpSZOcnMyaNWuoX79+tuuvXr06vr6+bm18oqOj2b59e7aXkVaPr68vq1evdo07depUjpcDsG7dOm699VY+/fRTunTpwnPPPZfjZYiINbSHR8QDrV69ml9//ZVu3boRERHB6tWrOXbsGPXq1csw7+bNm7nzzjt58sknadCgAYcPHwZwnd792GOP8fDDD+NwOOjQoQPR0dH8/vvvhISEMGjQoAzLCwwMZNiwYTz++OOUKlWKypUr8+abbxIfH89dd92V7dcQHBzMoEGDXMuJiIjghRdewMvL65KNi9MLCgrirrvu4vHHH6d06dJERETwzDPP4OXl/v/e7NmzGTVqFFu3bs10OXv37qVnz548/fTT9O/fn+rVq9O2bVvWrVtH8+bNs12PiFhDgUfEA4WEhLBs2TLGjRtHTEwMVapU4Z133qFHjx4Z5v3rr7+Ij4/nlVde4ZVXXnGN79SpE0uWLOHll18mPDycMWPGsHv3bsLCwmjevDlPP/10ls//+uuv43A4GDhwILGxsbRs2ZL58+dfsg3Rhd59913uv/9+rr/+ekJCQnjiiSc4cOAA/v7+OVrOW2+9RVxcHL169SI4OJhHH33UdRp5mujoaLZt25bp40+ePMm1117LjTfeyFNPPQVAmzZt6NGjB08//TS//PJLjuoRkYKna2mJSJFx5swZKlasyDvvvJOjvUUiItrDIyKF1vr169m6dSutW7cmOjqal156CYAbb7zR4spEpKhR4BGRQu3tt99m27Zt2O12WrRowfLly/P82lsi4vl0SEtEREQ8nk5LFxEREY+nwCMiIiIeT4FHREREPJ4Cj4iIiHg8BR4RERHxeAo8IiIi4vEUeERERMTjKfCIiIiIx1PgEREREY+nwCMiIiIeT4FHREREPJ4Cj4iIiHi8/weeMUgovJNQuQAAAABJRU5ErkJggg==", + "image/png": "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mTNndLeGDRuiV69euvvcHiWnefPmObrEuHLlCry9vQEAvr6+cHNz09secXFxOHbsmN72iImJwcmTJ3Xz7N69G1qtFk2aNCmBV1F+JCUlQanU/3kyMTGBVqsFwO0ht6Ja/8HBwdi/fz/S0tJ08+zYsQM1atQoFbvaALALgIpiyJAhwtbWVuzdu1dERkbqbklJSbp53nnnHeHl5SV2794tTpw4IYKDg0VwcLBuetYp5x06dBBnzpwRW7duFc7OzjzlvIhkP7tNCG6PkvT3338LU1NT8emnn4qrV6+KlStXCktLS7FixQrdPJ9//rmws7MTGzduFP/884/o0qWLwVOe69evL44dOyYOHjwoqlWrxlPOC6Fv376iUqVKui4A1q1bJ5ycnMRHH32km4fbo3jFx8eL06dPi9OnTwsAYs6cOeL06dPi1q1bQoiiWf8xMTHC1dVV9O7dW4SHh4vVq1cLS0tLdgFAJQ+AwdvSpUt18zx58kS8++67wt7eXlhaWopu3bqJyMhIveXcvHlTdOrUSVhYWAgnJycxevRokZaWVsKvpnx6NiRxe5SsP//8U9SuXVuo1WpRs2ZN8e233+pN12q1YtKkScLV1VWo1WrRrl07cfnyZb15Hj16JHr27Cmsra2FRqMR/fv3F/Hx8SX5MsqFuLg48f777wsvLy9hbm4uqlSpIiZMmKB3qji3R/Has2ePwd+Mvn37CiGKbv2fPXtWtGjRQqjValGpUiXx+eefl9RLNIpCiGxdmBIRERERAB6TRERERGQQQxIRERGRAQxJRERERAYwJBEREREZwJBEREREZABDEhEREZEBDElEREREBjAkERERERnAkERERERkAEMSEZHMbt++jdatWyMgIAB169bF2rVr5S6JiADwsiRERDKLjIzEvXv3EBgYiKioKAQFBeHKlSuwsrKSuzSiCo0tSURktNatW2PEiBGyPb8QAoMHD4aDgwMUCgXOnDlTpMs35vUVxzpwd3dHYGAgAMDNzQ1OTk6Ijo4u0ucgooIzlbsAIio71q1bBzMzM9mef+vWrVi2bBn27t2LKlWqwMnJqUiXL/frA4CTJ08iIyMDnp6estZBRAxJRFQADg4Osj7/tWvX4O7ujmbNmhXpclNTU6FSqWR/fdHR0ejTpw++++47WesgIgl3txGRnt9++w116tSBhYUFHB0dERISgsTERAD6u5pu3rwJhUKR49a6dWsAgFarxYwZM+Dr6wsLCwvUq1cPv/32W57PnZKSguHDh8PFxQXm5uZo0aIFjh8/DgDo168f3nvvPUREREChUMDHxyfX5cTHx6NXr16wsrKCu7s75s6dq1d769atMWzYMIwYMQJOTk4IDQ3N8foAIDExEX369IG1tTXc3d0xe/bsgq9QAL/88gssLCwQGRmpG9e/f3/UrVsXsbGxutfetWtXjB07tshDIBEVDkMSEelERkaiZ8+eGDBgAC5evIi9e/filVdegaHzOzw9PREZGam7nT59Go6OjmjZsiUAYMaMGfj555+xePFinD9/HiNHjsRbb72Fffv25fr8H330EX7//Xf89NNPOHXqFKpWrYrQ0FBER0dj/vz5mDZtGipXrozIyEhdeDJk1KhROHToEP744w/s2LEDBw4cwKlTp/Tm+emnn6BSqXDo0CEsXrzY4HI+/PBD7Nu3Dxs3bsT27duxd+/eHMtZtmwZFApFrrUAQI8ePVC9enV89tlnAIDJkydj586d+Ouvv2BrawshBPr164e2bduid+/eeS6LiEqQICLKdPLkSQFA3Lx50+D0Vq1aiffffz/H+CdPnogmTZqIl156SWRkZIjk5GRhaWkpDh8+rDffwIEDRc+ePQ0uOyEhQZiZmYmVK1fqxqWmpgoPDw8xc+ZMIYQQc+fOFd7e3nm+hri4OGFmZibWrl2rGxcTEyMsLS11tbdq1UrUr18/z9cXHx8vVCqV+PXXX3XTHz16JCwsLPTWwbp160SNGjXyrEkIIf7880+hVqvF9OnThb29vQgPD9dNO3DggFAoFKJevXq62z///JPvMomoePGYJCLSqVevHtq1a4c6deogNDQUHTp0wGuvvQZ7e/s8HzdgwADEx8djx44dUCqV+Pfff5GUlIT27dvrzZeamor69esbXMa1a9eQlpaG5s2b68aZmZmhcePGuHjxotGv4fr160hLS0Pjxo1142xtbVGjRg29+YKCgvJczrVr15CamoomTZroxjk4OORYTrdu3dCtW7d863rppZcQEBCAadOmYfv27ahVq5ZuWosWLaDVavNdBhGVLIYkItIxMTHBjh07cPjwYWzfvh1ff/01JkyYgGPHjsHX19fgY6ZPn45t27bh77//ho2NDQAgISEBALB582ZUqlRJb361Wl28L8JIJd0H0datW3Hp0iVkZGTA1dW1RJ+biAqHxyQRkR6FQoHmzZtj6tSpOH36NFQqFdavX29w3t9//x3Tpk3Dr7/+Cj8/P934gIAAqNVqREREoGrVqnq33E5t9/Pz0x0jlCUtLQ3Hjx9HQECA0fVXqVIFZmZmescsxcbG4sqVK0YvI6seMzMzHDt2TDfu8ePHBV4OAJw6dQqvv/46fvjhB7Rr1w6TJk0q8DKIqOSxJYmIdI4dO4Zdu3ahQ4cOcHFxwbFjx/DgwQP4+/vnmDc8PBx9+vTBmDFjUKtWLURFRQGA7lT6Dz74ACNHjoRWq0WLFi0QGxuLQ4cOQaPRoG/fvjmWZ2VlhSFDhuDDDz+Eg4MDvLy8MHPmTCQlJWHgwIFGvwYbGxv07dtXtxwXFxdMnjwZSqUy3wOss7O2tsbAgQPx4YcfwtHRES4uLpgwYQKUSv3/LdevX49x48bh0qVLBpdz8+ZNdO7cGePHj0fPnj1RpUoVBAcH49SpU2jQoIHR9RBRyWNIIiIdjUaD/fv3Y968eYiLi4O3tzdmz56NTp065Zj3xIkTSEpKwvTp0zF9+nTd+FatWmHv3r345JNP4OzsjBkzZuD69euws7NDgwYNMH78+Fyf//PPP4dWq0Xv3r0RHx+Phg0bYtu2bfkeE/WsOXPm4J133sFLL70EjUaDjz76CLdv34a5uXmBljNr1iwkJCQgLCwMNjY2GD16tO6U/SyxsbG4fPmywcdHR0ejY8eO6NKlC8aOHQsAaNKkCTp16oTx48dj69atBaqHiEoWr91GROVeYmIiKlWqhNmzZxeoVYqIKja2JBFRuXP69GlcunQJjRs3RmxsLKZNmwYA6NKli8yVEVFZwpBEROXSl19+icuXL0OlUiEoKAgHDhwo8mu9EVH5xt1tRERERAawCwAiIiIiAxiSiIiIiAxgSCIiIiIygCGJiIiIyACGJCIiIiIDGJKIiIiIDGBIIiIiIjKAIYmIiIjIAIYkIiIiIgMYkoiIiIgMYEgiIiIiMoAhiYiIiMiA/wPVAOvuoq24HgAAAABJRU5ErkJggg==", "text/plain": [ "
" ] @@ -383,7 +408,9 @@ { "cell_type": "code", "execution_count": 14, - "metadata": {}, + "metadata": { + "tags": [] + }, "outputs": [], "source": [ "from HARK.distribution import expected" @@ -392,7 +419,9 @@ { "cell_type": "code", "execution_count": 15, - "metadata": {}, + "metadata": { + "tags": [] + }, "outputs": [ { "data": { @@ -431,12 +460,14 @@ { "cell_type": "code", "execution_count": 16, - "metadata": {}, + "metadata": { + "tags": [] + }, "outputs": [ { "data": { "text/plain": [ - "3.7147215033526995" + "3.7147215033526537" ] }, "execution_count": 16, @@ -447,13 +478,6 @@ "source": [ "expected(func=lambda x: 1 / x[0] + x[1], dist=IncShkDstn)" ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] } ], "metadata": { diff --git a/examples/FrameAgentType/FrameAgentType Demo.ipynb b/examples/FrameAgentType/FrameAgentType Demo.ipynb index 5bb5d6fc3..0a74a4f16 100644 --- a/examples/FrameAgentType/FrameAgentType Demo.ipynb +++ b/examples/FrameAgentType/FrameAgentType Demo.ipynb @@ -10,7 +10,6 @@ "import HARK.ConsumptionSaving.ConsPortfolioModel as cpm\n", "\n", "from HARK.frame import Frame, draw_frame_model\n", - "import numpy as np\n", "\n", "from HARK.rewards import (\n", " CRRAutility,\n", diff --git a/examples/FrameAgentType/FrameModels.ipynb b/examples/FrameAgentType/FrameModels.ipynb index 379053901..40e1fc54a 100644 --- a/examples/FrameAgentType/FrameModels.ipynb +++ b/examples/FrameAgentType/FrameModels.ipynb @@ -9,19 +9,16 @@ "source": [ "from HARK.frame import (\n", " BackwardFrameReference,\n", - " ForwardFrameReference,\n", " Frame,\n", " FrameAgentType,\n", " FrameModel,\n", " draw_frame_model,\n", ")\n", "\n", - "from HARK.distribution import combine_indep_dstns, add_discrete_outcome_constant_mean\n", "from HARK.distribution import (\n", " IndexDistribution,\n", " Lognormal,\n", - " MeanOneLogNormal,\n", - " Bernoulli, # Random draws for simulating agents\n", + " MeanOneLogNormal, # Random draws for simulating agents\n", ")\n", "\n", "from HARK.rewards import (\n", @@ -874,7 +871,7 @@ " {\n", " \"mean\": init_parameters[\"RiskyAvg\"],\n", " \"std\": init_parameters[\"RiskyStd\"],\n", - " }\n", + " },\n", " # seed=self.RNG.integers(0, 2 ** 31 - 1) : TODO: Seed logic\n", " ).discretize(init_parameters[\"RiskyCount\"], method=\"equiprobable\"),\n", " aggregate=True,\n", diff --git a/examples/Gentle-Intro/Gentle-Intro-To-HARK.ipynb b/examples/Gentle-Intro/Gentle-Intro-To-HARK.ipynb index 6657619dc..0832dfbb0 100644 --- a/examples/Gentle-Intro/Gentle-Intro-To-HARK.ipynb +++ b/examples/Gentle-Intro/Gentle-Intro-To-HARK.ipynb @@ -25,9 +25,6 @@ "# The most common problem beginners have is to execute a cell before all its predecessors\n", "# If you do this, you can restart the kernel (see the \"Kernel\" menu above) and start over\n", "\n", - "import matplotlib.pyplot as plt\n", - "import numpy as np\n", - "import HARK\n", "from copy import deepcopy\n", "\n", "mystr = lambda number: \"{:.4f}\".format(number)\n", diff --git a/examples/HowWeSolveIndShockConsumerType/HowWeSolveIndShockConsumerType.ipynb b/examples/HowWeSolveIndShockConsumerType/HowWeSolveIndShockConsumerType.ipynb index efdb7eddc..1b52f63ae 100644 --- a/examples/HowWeSolveIndShockConsumerType/HowWeSolveIndShockConsumerType.ipynb +++ b/examples/HowWeSolveIndShockConsumerType/HowWeSolveIndShockConsumerType.ipynb @@ -109,7 +109,6 @@ " init_lifecycle,\n", ")\n", "import numpy as np\n", - "import matplotlib.pyplot as plt\n", "\n", "LifecycleExample = IndShockConsumerType(**init_lifecycle)\n", "LifecycleExample.cycles = (\n", diff --git a/examples/Journeys/AzureMachineLearning.ipynb b/examples/Journeys/AzureMachineLearning.ipynb index 66e35805a..efa7d2b5e 100644 --- a/examples/Journeys/AzureMachineLearning.ipynb +++ b/examples/Journeys/AzureMachineLearning.ipynb @@ -68,9 +68,6 @@ } ], "source": [ - "import matplotlib.pyplot as plt\n", - "import numpy as np\n", - "\n", "# Initial imports and notebook setup, click arrow to show\n", "from HARK.ConsumptionSaving.ConsIndShockModelFast import IndShockConsumerTypeFast\n", "from HARK.utilities import plot_funcs_der, plot_funcs\n", diff --git a/examples/Journeys/Journey-Policymaker.ipynb b/examples/Journeys/Journey-Policymaker.ipynb index a8cfb18a6..df7c10eba 100644 --- a/examples/Journeys/Journey-Policymaker.ipynb +++ b/examples/Journeys/Journey-Policymaker.ipynb @@ -404,7 +404,7 @@ } ], "source": [ - "from HARK.utilities import get_lorenz_shares, get_percentiles\n", + "from HARK.utilities import get_lorenz_shares\n", "\n", "pctiles = np.linspace(0.001, 0.999, 200)\n", "sim_Lorenz_points = get_lorenz_shares(\n", diff --git a/examples/Journeys/Quickstart_tutorial/Quick_start_with_solution.ipynb b/examples/Journeys/Quickstart_tutorial/Quick_start_with_solution.ipynb index e917b0cd9..3d86c7612 100644 --- a/examples/Journeys/Quickstart_tutorial/Quick_start_with_solution.ipynb +++ b/examples/Journeys/Quickstart_tutorial/Quick_start_with_solution.ipynb @@ -99,17 +99,15 @@ }, "outputs": [], "source": [ - "\n", "# import sys\n", "# import os\n", "# sys.path.insert(0, os.path.abspath('../../../.'))\n", "import matplotlib.pyplot as plt\n", "import numpy as np\n", - "import HARK\n", "\n", "from copy import deepcopy\n", "from HARK.ConsumptionSaving.ConsIndShockModel import *\n", - "from HARK.utilities import plot_funcs_der, plot_funcs" + "from HARK.utilities import plot_funcs" ] }, { @@ -3284,8 +3282,7 @@ }, "outputs": [], "source": [ - "\n", - "from HARK.utilities import get_lorenz_shares, get_percentiles" + "from HARK.utilities import get_lorenz_shares" ] }, { diff --git a/examples/LabeledModels/LabeledModels.ipynb b/examples/LabeledModels/LabeledModels.ipynb index 5e3f333d1..da73a7fff 100644 --- a/examples/LabeledModels/LabeledModels.ipynb +++ b/examples/LabeledModels/LabeledModels.ipynb @@ -23,7 +23,6 @@ "from types import SimpleNamespace\n", "\n", "import estimagic as em\n", - "import matplotlib.pyplot as plt\n", "import numpy as np\n", "import xarray as xr\n", "from HARK.ConsumptionSaving.ConsIndShockModel import PerfForesightConsumerType\n", diff --git a/examples/LifecycleModel/Cycles_tutorial.ipynb b/examples/LifecycleModel/Cycles_tutorial.ipynb index a3952818c..b362dec6b 100644 --- a/examples/LifecycleModel/Cycles_tutorial.ipynb +++ b/examples/LifecycleModel/Cycles_tutorial.ipynb @@ -18,13 +18,10 @@ "outputs": [], "source": [ "# Attempt at combining the imports from both notebooks -- it works!\n", - "import matplotlib.pyplot as plt\n", "import numpy as np\n", - "import HARK\n", "\n", - "from copy import deepcopy\n", "from HARK.ConsumptionSaving.ConsIndShockModel import *\n", - "from HARK.utilities import plot_funcs_der, plot_funcs\n", + "from HARK.utilities import plot_funcs\n", "\n", "mystr = lambda number: \"{:.4f}\".format(number)" ] diff --git a/examples/LifecycleModel/LifecycleModel.ipynb b/examples/LifecycleModel/LifecycleModel.ipynb index b071ab50d..e57fab58b 100644 --- a/examples/LifecycleModel/LifecycleModel.ipynb +++ b/examples/LifecycleModel/LifecycleModel.ipynb @@ -42,7 +42,7 @@ "\n", "import HARK.ConsumptionSaving.ConsIndShockModel as Model # The consumption-saving micro model\n", "import EstimationParameters as Params # Parameters for the consumer type and the estimation\n", - "from HARK.utilities import plot_funcs_der, plot_funcs # Some tools\n", + "from HARK.utilities import plot_funcs # Some tools\n", "\n", "import numpy as np" ] diff --git a/requirements/dev.txt b/requirements/dev.txt index c1c6b0c4e..9e8c1d67f 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -2,4 +2,5 @@ estimagic nbval pre-commit pytest -pytest-xdist \ No newline at end of file +pytest-xdist +ruff diff --git a/requirements/doc.txt b/requirements/doc.txt index 9aa79df64..a2717f479 100644 --- a/requirements/doc.txt +++ b/requirements/doc.txt @@ -1,11 +1,11 @@ -sphinx>=6.1 - -# theme requirements -pydata-sphinx-theme # extension requirements ipython # for the Pygments lexer myst-parser>=2 nbsphinx>=0.8 + +# theme requirements +pydata-sphinx-theme +sphinx>=6.1 sphinx-copybutton sphinx-design diff --git a/ruff.toml b/ruff.toml new file mode 100644 index 000000000..84d7322cf --- /dev/null +++ b/ruff.toml @@ -0,0 +1,3 @@ +include = ["*.ipynb"] +# ignore F401 for now: https://github.com/astral-sh/ruff/issues/8354 +ignore = ["E731", "E721", "E402", "F841", "F821", "F405", "F403", "F401"] diff --git a/tools/nb_exec.py b/tools/nb_exec.py index e8b5868e2..8c2e299f3 100644 --- a/tools/nb_exec.py +++ b/tools/nb_exec.py @@ -24,29 +24,31 @@ def run_notebook(notebook_file: Path): rel_file_name = notebook_file.relative_to(ROOT_DIR).as_posix() - print(f'{rel_file_name}: Loading notebook') + print(f"{rel_file_name}: Loading notebook") try: # Journey-PhD and LifecycleModel expect execution from their own directory os.chdir(notebook_file.parent) nb = nbformat.read(notebook_file, as_version=4) - client = NotebookClient(nb, timeout=600, kernel_name='python3', record_timing=False) - print(f'{rel_file_name}: Executing') + client = NotebookClient( + nb, timeout=600, kernel_name="python3", record_timing=False + ) + print(f"{rel_file_name}: Executing") start = time.perf_counter() client.execute() elapsed = time.perf_counter() - start - print(f'{rel_file_name}: Writing') + print(f"{rel_file_name}: Writing") nbformat.write(nb, notebook_file) - print(f'{rel_file_name}: Finished (executed in {elapsed:.2f}s)') + print(f"{rel_file_name}: Finished (executed in {elapsed:.2f}s)") del nb, client, start, elapsed except Exception as err: - print(f'{rel_file_name}: Failed to execute\n {err}', file=sys.stderr) + print(f"{rel_file_name}: Failed to execute\n {err}", file=sys.stderr) -if __name__ == '__main__': +if __name__ == "__main__": if len(sys.argv) > 1: notebooks = (Path(p).resolve() for p in sys.argv[1:]) else: - notebooks = ROOT_DIR.joinpath('examples').rglob('*.ipynb') + notebooks = ROOT_DIR.joinpath("examples").rglob("*.ipynb") with multiprocessing.Pool() as pool: pool.map(run_notebook, notebooks)