├── .gitignore
├── lectures
├── MyGraph5.jpg
├── MyGraph5.png
├── dataBHS.mat
├── _static
│ ├── .DS_Store
│ ├── qe-logo-large.png
│ ├── lectures-favicon.ico
│ ├── lecture_specific
│ │ ├── amss3
│ │ │ ├── amss3_g1.png
│ │ │ ├── amss3_g2.png
│ │ │ └── amss3_g3.png
│ │ ├── robustness
│ │ │ ├── kg.png
│ │ │ ├── kg0.png
│ │ │ └── kg_small_theta.png
│ │ ├── coase
│ │ │ ├── allocation.png
│ │ │ ├── subcontracting.png
│ │ │ ├── allocation.tex
│ │ │ └── subcontracting.tex
│ │ ├── lqramsey
│ │ │ └── firenze.pdf
│ │ ├── arma
│ │ │ └── time_series_book.pdf
│ │ ├── estspec
│ │ │ ├── periodogram1.png
│ │ │ ├── window_smoothing.png
│ │ │ └── ar_smoothed_periodogram.png
│ │ ├── markov_perf
│ │ │ ├── judd_fig1.png
│ │ │ ├── judd_fig2.png
│ │ │ ├── mpe_vs_monopolist.png
│ │ │ └── duopoly_mpe.py
│ │ ├── matsuyama
│ │ │ ├── matsuyama_14.png
│ │ │ └── matsuyama_18.png
│ │ ├── orth_proj
│ │ │ ├── orth_proj_def1.png
│ │ │ ├── orth_proj_def2.png
│ │ │ ├── orth_proj_def3.png
│ │ │ ├── orth_proj_thm1.png
│ │ │ ├── orth_proj_thm2.pdf
│ │ │ ├── orth_proj_thm2.png
│ │ │ ├── orth_proj_thm3.png
│ │ │ ├── orth_proj_def1.tex
│ │ │ ├── orth_proj_def2.tex
│ │ │ ├── orth_proj_def3.tex
│ │ │ ├── orth_proj_thm1.tex
│ │ │ ├── orth_proj_thm2.tex
│ │ │ └── orth_proj_thm3.tex
│ │ ├── troubleshooting
│ │ │ └── launch.png
│ │ ├── arellano
│ │ │ ├── arellano_bond_prices.png
│ │ │ ├── arellano_time_series.png
│ │ │ ├── arellano_value_funcs.png
│ │ │ ├── arellano_bond_prices_2.png
│ │ │ └── arellano_default_probs.png
│ │ ├── lucas_model
│ │ │ ├── solution_mass_ex2.png
│ │ │ └── lucastree.py
│ │ ├── stationary_densities
│ │ │ ├── ECTA6180.pdf
│ │ │ ├── solution_statd_ex1.png
│ │ │ └── solution_statd_ex2.png
│ │ ├── discrete_dp
│ │ │ ├── finite_dp_simple_og.png
│ │ │ └── finite_dp_simple_og2.png
│ │ ├── asset_pricing_lph
│ │ │ └── AssetPricing_v1.jpg
│ │ ├── opt_tax_recur
│ │ │ ├── log_utility.py
│ │ │ ├── crra_utility.py
│ │ │ ├── sequential_allocation.py
│ │ │ └── recursive_allocation.py
│ │ ├── amss2
│ │ │ ├── log_utility.py
│ │ │ ├── crra_utility.py
│ │ │ ├── utilities.py
│ │ │ ├── sequential_allocation.py
│ │ │ └── recursive_allocation.py
│ │ ├── amss
│ │ │ ├── utilities.py
│ │ │ └── recursive_allocation.py
│ │ ├── lu_tricks
│ │ │ └── control_and_filter.py
│ │ └── match_transport
│ │ │ └── acs_data_summary.csv
│ ├── includes
│ │ ├── lecture_howto_py.raw
│ │ └── header.raw
│ └── downloads
│ │ └── amss_environment.yml
├── entropy_glogg.jpg
├── entropy_glogg.png
├── entropy_1_over_15.jpg
├── entropy_1_over_15.png
├── entropy_1_over_95.jpg
├── entropy_1_over_95.png
├── zreferences.md
├── intro.md
├── status.md
├── _toc.yml
├── troubleshooting.md
├── _config.yml
├── lucas_asset_pricing_dles.md
├── irfs_in_hall_model.md
├── permanent_income_dles.md
├── tax_smoothing_3.md
├── rosen_schooling_model.md
├── hs_invertibility_example.md
├── muth_kalman.md
└── cattle_cycles.md
├── _notebook_repo
├── environment.yml
└── README.md
├── environment.yml
├── lychee.toml
├── .github
├── dependabot.yml
└── workflows
│ ├── style-guide.yml
│ ├── linkcheck.yml
│ ├── cache.yml
│ ├── ci.yml
│ └── publish.yml
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | _build/
2 | .DS_Store
3 | .ipynb_checkpoints/
4 | .virtual_documents/
--------------------------------------------------------------------------------
/lectures/MyGraph5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuantEcon/lecture-python-advanced.myst/main/lectures/MyGraph5.jpg
--------------------------------------------------------------------------------
/lectures/MyGraph5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuantEcon/lecture-python-advanced.myst/main/lectures/MyGraph5.png
--------------------------------------------------------------------------------
/lectures/dataBHS.mat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuantEcon/lecture-python-advanced.myst/main/lectures/dataBHS.mat
--------------------------------------------------------------------------------
/lectures/_static/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuantEcon/lecture-python-advanced.myst/main/lectures/_static/.DS_Store
--------------------------------------------------------------------------------
/lectures/entropy_glogg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuantEcon/lecture-python-advanced.myst/main/lectures/entropy_glogg.jpg
--------------------------------------------------------------------------------
/lectures/entropy_glogg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuantEcon/lecture-python-advanced.myst/main/lectures/entropy_glogg.png
--------------------------------------------------------------------------------
/lectures/entropy_1_over_15.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuantEcon/lecture-python-advanced.myst/main/lectures/entropy_1_over_15.jpg
--------------------------------------------------------------------------------
/lectures/entropy_1_over_15.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuantEcon/lecture-python-advanced.myst/main/lectures/entropy_1_over_15.png
--------------------------------------------------------------------------------
/lectures/entropy_1_over_95.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuantEcon/lecture-python-advanced.myst/main/lectures/entropy_1_over_95.jpg
--------------------------------------------------------------------------------
/lectures/entropy_1_over_95.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuantEcon/lecture-python-advanced.myst/main/lectures/entropy_1_over_95.png
--------------------------------------------------------------------------------
/lectures/_static/qe-logo-large.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuantEcon/lecture-python-advanced.myst/main/lectures/_static/qe-logo-large.png
--------------------------------------------------------------------------------
/lectures/_static/lectures-favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuantEcon/lecture-python-advanced.myst/main/lectures/_static/lectures-favicon.ico
--------------------------------------------------------------------------------
/_notebook_repo/environment.yml:
--------------------------------------------------------------------------------
1 | name: lecture-python-advanced
2 | channels:
3 | - default
4 | dependencies:
5 | - python=3.8
6 | - anaconda
7 |
8 |
--------------------------------------------------------------------------------
/lectures/_static/lecture_specific/amss3/amss3_g1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuantEcon/lecture-python-advanced.myst/main/lectures/_static/lecture_specific/amss3/amss3_g1.png
--------------------------------------------------------------------------------
/lectures/_static/lecture_specific/amss3/amss3_g2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuantEcon/lecture-python-advanced.myst/main/lectures/_static/lecture_specific/amss3/amss3_g2.png
--------------------------------------------------------------------------------
/lectures/_static/lecture_specific/amss3/amss3_g3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuantEcon/lecture-python-advanced.myst/main/lectures/_static/lecture_specific/amss3/amss3_g3.png
--------------------------------------------------------------------------------
/lectures/_static/lecture_specific/robustness/kg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuantEcon/lecture-python-advanced.myst/main/lectures/_static/lecture_specific/robustness/kg.png
--------------------------------------------------------------------------------
/lectures/_static/lecture_specific/robustness/kg0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuantEcon/lecture-python-advanced.myst/main/lectures/_static/lecture_specific/robustness/kg0.png
--------------------------------------------------------------------------------
/lectures/_static/lecture_specific/coase/allocation.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuantEcon/lecture-python-advanced.myst/main/lectures/_static/lecture_specific/coase/allocation.png
--------------------------------------------------------------------------------
/lectures/_static/lecture_specific/lqramsey/firenze.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuantEcon/lecture-python-advanced.myst/main/lectures/_static/lecture_specific/lqramsey/firenze.pdf
--------------------------------------------------------------------------------
/lectures/_static/lecture_specific/arma/time_series_book.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuantEcon/lecture-python-advanced.myst/main/lectures/_static/lecture_specific/arma/time_series_book.pdf
--------------------------------------------------------------------------------
/lectures/_static/lecture_specific/coase/subcontracting.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuantEcon/lecture-python-advanced.myst/main/lectures/_static/lecture_specific/coase/subcontracting.png
--------------------------------------------------------------------------------
/lectures/_static/lecture_specific/estspec/periodogram1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuantEcon/lecture-python-advanced.myst/main/lectures/_static/lecture_specific/estspec/periodogram1.png
--------------------------------------------------------------------------------
/lectures/_static/lecture_specific/markov_perf/judd_fig1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuantEcon/lecture-python-advanced.myst/main/lectures/_static/lecture_specific/markov_perf/judd_fig1.png
--------------------------------------------------------------------------------
/lectures/_static/lecture_specific/markov_perf/judd_fig2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuantEcon/lecture-python-advanced.myst/main/lectures/_static/lecture_specific/markov_perf/judd_fig2.png
--------------------------------------------------------------------------------
/lectures/_static/lecture_specific/estspec/window_smoothing.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuantEcon/lecture-python-advanced.myst/main/lectures/_static/lecture_specific/estspec/window_smoothing.png
--------------------------------------------------------------------------------
/lectures/_static/lecture_specific/matsuyama/matsuyama_14.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuantEcon/lecture-python-advanced.myst/main/lectures/_static/lecture_specific/matsuyama/matsuyama_14.png
--------------------------------------------------------------------------------
/lectures/_static/lecture_specific/matsuyama/matsuyama_18.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuantEcon/lecture-python-advanced.myst/main/lectures/_static/lecture_specific/matsuyama/matsuyama_18.png
--------------------------------------------------------------------------------
/lectures/_static/lecture_specific/orth_proj/orth_proj_def1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuantEcon/lecture-python-advanced.myst/main/lectures/_static/lecture_specific/orth_proj/orth_proj_def1.png
--------------------------------------------------------------------------------
/lectures/_static/lecture_specific/orth_proj/orth_proj_def2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuantEcon/lecture-python-advanced.myst/main/lectures/_static/lecture_specific/orth_proj/orth_proj_def2.png
--------------------------------------------------------------------------------
/lectures/_static/lecture_specific/orth_proj/orth_proj_def3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuantEcon/lecture-python-advanced.myst/main/lectures/_static/lecture_specific/orth_proj/orth_proj_def3.png
--------------------------------------------------------------------------------
/lectures/_static/lecture_specific/orth_proj/orth_proj_thm1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuantEcon/lecture-python-advanced.myst/main/lectures/_static/lecture_specific/orth_proj/orth_proj_thm1.png
--------------------------------------------------------------------------------
/lectures/_static/lecture_specific/orth_proj/orth_proj_thm2.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuantEcon/lecture-python-advanced.myst/main/lectures/_static/lecture_specific/orth_proj/orth_proj_thm2.pdf
--------------------------------------------------------------------------------
/lectures/_static/lecture_specific/orth_proj/orth_proj_thm2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuantEcon/lecture-python-advanced.myst/main/lectures/_static/lecture_specific/orth_proj/orth_proj_thm2.png
--------------------------------------------------------------------------------
/lectures/_static/lecture_specific/orth_proj/orth_proj_thm3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuantEcon/lecture-python-advanced.myst/main/lectures/_static/lecture_specific/orth_proj/orth_proj_thm3.png
--------------------------------------------------------------------------------
/lectures/_static/lecture_specific/troubleshooting/launch.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuantEcon/lecture-python-advanced.myst/main/lectures/_static/lecture_specific/troubleshooting/launch.png
--------------------------------------------------------------------------------
/lectures/_static/lecture_specific/robustness/kg_small_theta.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuantEcon/lecture-python-advanced.myst/main/lectures/_static/lecture_specific/robustness/kg_small_theta.png
--------------------------------------------------------------------------------
/lectures/_static/lecture_specific/arellano/arellano_bond_prices.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuantEcon/lecture-python-advanced.myst/main/lectures/_static/lecture_specific/arellano/arellano_bond_prices.png
--------------------------------------------------------------------------------
/lectures/_static/lecture_specific/arellano/arellano_time_series.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuantEcon/lecture-python-advanced.myst/main/lectures/_static/lecture_specific/arellano/arellano_time_series.png
--------------------------------------------------------------------------------
/lectures/_static/lecture_specific/arellano/arellano_value_funcs.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuantEcon/lecture-python-advanced.myst/main/lectures/_static/lecture_specific/arellano/arellano_value_funcs.png
--------------------------------------------------------------------------------
/lectures/_static/lecture_specific/lucas_model/solution_mass_ex2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuantEcon/lecture-python-advanced.myst/main/lectures/_static/lecture_specific/lucas_model/solution_mass_ex2.png
--------------------------------------------------------------------------------
/lectures/_static/lecture_specific/markov_perf/mpe_vs_monopolist.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuantEcon/lecture-python-advanced.myst/main/lectures/_static/lecture_specific/markov_perf/mpe_vs_monopolist.png
--------------------------------------------------------------------------------
/lectures/_static/lecture_specific/stationary_densities/ECTA6180.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuantEcon/lecture-python-advanced.myst/main/lectures/_static/lecture_specific/stationary_densities/ECTA6180.pdf
--------------------------------------------------------------------------------
/lectures/_static/lecture_specific/arellano/arellano_bond_prices_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuantEcon/lecture-python-advanced.myst/main/lectures/_static/lecture_specific/arellano/arellano_bond_prices_2.png
--------------------------------------------------------------------------------
/lectures/_static/lecture_specific/arellano/arellano_default_probs.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuantEcon/lecture-python-advanced.myst/main/lectures/_static/lecture_specific/arellano/arellano_default_probs.png
--------------------------------------------------------------------------------
/lectures/_static/lecture_specific/discrete_dp/finite_dp_simple_og.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuantEcon/lecture-python-advanced.myst/main/lectures/_static/lecture_specific/discrete_dp/finite_dp_simple_og.png
--------------------------------------------------------------------------------
/lectures/_static/lecture_specific/estspec/ar_smoothed_periodogram.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuantEcon/lecture-python-advanced.myst/main/lectures/_static/lecture_specific/estspec/ar_smoothed_periodogram.png
--------------------------------------------------------------------------------
/lectures/_static/lecture_specific/asset_pricing_lph/AssetPricing_v1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuantEcon/lecture-python-advanced.myst/main/lectures/_static/lecture_specific/asset_pricing_lph/AssetPricing_v1.jpg
--------------------------------------------------------------------------------
/lectures/_static/lecture_specific/discrete_dp/finite_dp_simple_og2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuantEcon/lecture-python-advanced.myst/main/lectures/_static/lecture_specific/discrete_dp/finite_dp_simple_og2.png
--------------------------------------------------------------------------------
/lectures/_static/lecture_specific/stationary_densities/solution_statd_ex1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuantEcon/lecture-python-advanced.myst/main/lectures/_static/lecture_specific/stationary_densities/solution_statd_ex1.png
--------------------------------------------------------------------------------
/lectures/_static/lecture_specific/stationary_densities/solution_statd_ex2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuantEcon/lecture-python-advanced.myst/main/lectures/_static/lecture_specific/stationary_densities/solution_statd_ex2.png
--------------------------------------------------------------------------------
/lectures/zreferences.md:
--------------------------------------------------------------------------------
1 | ---
2 | jupytext:
3 | text_representation:
4 | extension: .md
5 | format_name: myst
6 | kernelspec:
7 | display_name: Python 3
8 | language: python
9 | name: python3
10 | ---
11 |
12 | (references)=
13 | # References
14 |
15 | ```{bibliography} _static/quant-econ.bib
16 | ```
17 |
18 |
--------------------------------------------------------------------------------
/lectures/_static/includes/lecture_howto_py.raw:
--------------------------------------------------------------------------------
1 | .. raw:: html
2 |
3 |
8 |
--------------------------------------------------------------------------------
/lectures/_static/includes/header.raw:
--------------------------------------------------------------------------------
1 | .. raw:: html
2 |
3 |
8 |
--------------------------------------------------------------------------------
/_notebook_repo/README.md:
--------------------------------------------------------------------------------
1 | # lecture-python-advanced.notebooks
2 |
3 | [](https://mybinder.org/v2/gh/QuantEcon/lecture-python-advanced.notebooks/master)
4 |
5 | Notebooks for https://python-advanced.quantecon.org
6 |
7 | **Note:** This README should be edited [here](https://github.com/quantecon/lecture-python-advanced.myst/_notebook_repo)
8 |
--------------------------------------------------------------------------------
/lectures/intro.md:
--------------------------------------------------------------------------------
1 | ---
2 | jupytext:
3 | text_representation:
4 | extension: .md
5 | format_name: myst
6 | kernelspec:
7 | display_name: Python 3
8 | language: python
9 | name: python3
10 | ---
11 |
12 | # Advanced Quantitative Economics with Python
13 |
14 | This website presents a set of advanced lectures on quantitative economic modeling.
15 |
16 | ```{tableofcontents}
17 | ```
--------------------------------------------------------------------------------
/environment.yml:
--------------------------------------------------------------------------------
1 | name: quantecon
2 | channels:
3 | - default
4 | dependencies:
5 | - python=3.13
6 | - anaconda=2025.06
7 | - pip
8 | - pip:
9 | - jupyter-book==1.0.4post1
10 | - quantecon-book-theme==0.9.3
11 | - sphinx-tojupyter==0.3.1
12 | - sphinxext-rediraffe==0.2.7
13 | - sphinx-exercise==1.0.1
14 | - sphinx-proof==0.2.1
15 | - sphinxcontrib-youtube==1.4.1
16 | - sphinx-togglebutton==0.3.2
17 | - sphinx-reredirects==0.1.4
18 |
19 |
20 |
--------------------------------------------------------------------------------
/lychee.toml:
--------------------------------------------------------------------------------
1 | # Lychee link checker configuration
2 | # For more configuration options, see: https://github.com/lycheeverse/lychee
3 |
4 | # Accept specific HTTP status codes that are normally treated as errors
5 | accept = [403, 503]
6 |
7 | # Exclude paths from checking
8 | # This excludes the webpack-macros.html file which contains unprocessed Jinja2 template variables
9 | # like {{ pathto() }} that cause false positives in link checking
10 | exclude_path = ["_static/webpack-macros.html"]
--------------------------------------------------------------------------------
/lectures/_static/downloads/amss_environment.yml:
--------------------------------------------------------------------------------
1 | name: amss
2 | channels:
3 | - default
4 | - conda-forge
5 | dependencies:
6 | - pip
7 | - python
8 | - jupyter
9 | - jupyterlab
10 | - nbconvert
11 | - pandoc
12 | - pandas
13 | - numba
14 | - scipy=1.4.1
15 | - numpy
16 | - matplotlib
17 | - networkx
18 | - sphinx=2.4.4
19 | - interpolation
20 | - seaborn
21 | - pip:
22 | - sphinxcontrib-jupyter
23 | - sphinxcontrib-bibtex
24 | - quantecon
25 | - joblib
26 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | # To get started with Dependabot version updates, you'll need to specify which
2 | # package ecosystems to update and where the package manifests are located.
3 | # Please see the documentation for all configuration options:
4 | # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
5 |
6 | version: 2
7 | updates:
8 | - package-ecosystem: github-actions
9 | directory: /
10 | commit-message:
11 | prefix: ⬆️
12 | schedule:
13 | interval: weekly
14 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Advanced Quantitative Economics with Python
2 |
3 | This website presents a set of advanced lectures on quantitative economic modeling.
4 |
5 | ## Jupyter notebooks
6 |
7 | Jupyter notebook versions of each lecture are available for download
8 | via the website.
9 |
10 | ## Contributions
11 |
12 | To comment on the lectures please add to or open an issue in the issue tracker (see above).
13 |
14 | We welcome pull requests!
15 |
16 | Please read the [QuantEcon style guide](https://manual.quantecon.org/intro.html) first, so that you can match our style.
17 |
--------------------------------------------------------------------------------
/.github/workflows/style-guide.yml:
--------------------------------------------------------------------------------
1 | name: Style Guide Comment Trigger
2 | on:
3 | issues:
4 | types: [opened, edited]
5 | issue_comment:
6 | types: [created]
7 |
8 | jobs:
9 | check-trigger:
10 | if: contains(github.event.comment.body, '@qe-style-checker')
11 | runs-on: ubuntu-latest
12 | steps:
13 | - uses: QuantEcon/action-style-guide@v0.5
14 | with:
15 | mode: 'single'
16 | lectures-path: 'lectures/'
17 | anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }}
18 | github-token: ${{ secrets.GITHUB_TOKEN }}
19 | comment-body: ${{ github.event.comment.body || github.event.issue.body }}
20 |
--------------------------------------------------------------------------------
/lectures/status.md:
--------------------------------------------------------------------------------
1 | ---
2 | jupytext:
3 | text_representation:
4 | extension: .md
5 | format_name: myst
6 | kernelspec:
7 | display_name: Python 3
8 | language: python
9 | name: python3
10 | ---
11 |
12 | # Execution Statistics
13 |
14 | This table contains the latest execution statistics.
15 |
16 | ```{nb-exec-table}
17 | ```
18 |
19 | (status:machine-details)=
20 |
21 | These lectures are built on `linux` instances through `github actions`.
22 |
23 | These lectures are using the following python version
24 |
25 | ```{code-cell} ipython
26 | !python --version
27 | ```
28 |
29 | and the following package versions
30 |
31 | ```{code-cell} ipython
32 | :tags: [hide-output]
33 | !conda list
34 | ```
--------------------------------------------------------------------------------
/lectures/_static/lecture_specific/opt_tax_recur/log_utility.py:
--------------------------------------------------------------------------------
1 | log_util_data = [
2 | ('β', float64),
3 | ('ψ', float64)
4 | ]
5 |
6 | @jitclass(log_util_data)
7 | class LogUtility:
8 |
9 | def __init__(self,
10 | β=0.9,
11 | ψ=0.69):
12 |
13 | self.β, self.ψ = β, ψ
14 |
15 | # Utility function
16 | def U(self, c, l):
17 | return np.log(c) + self.ψ * np.log(l)
18 |
19 | # Derivatives of utility function
20 | def Uc(self, c, l):
21 | return 1 / c
22 |
23 | def Ucc(self, c, l):
24 | return -c**(-2)
25 |
26 | def Ul(self, c, l):
27 | return self.ψ / l
28 |
29 | def Ull(self, c, l):
30 | return -self.ψ / l**2
31 |
32 | def Ucl(self, c, l):
33 | return 0
34 |
35 | def Ulc(self, c, l):
36 | return 0
37 |
--------------------------------------------------------------------------------
/lectures/_static/lecture_specific/amss2/log_utility.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 |
3 | class LogUtility:
4 |
5 | def __init__(self,
6 | β=0.9,
7 | ψ=0.69,
8 | π=np.full((2, 2), 0.5),
9 | G=np.array([0.1, 0.2]),
10 | Θ=np.ones(2),
11 | transfers=False):
12 |
13 | self.β, self.ψ, self.π = β, ψ, π
14 | self.G, self.Θ, self.transfers = G, Θ, transfers
15 |
16 | # Utility function
17 | def U(self, c, n):
18 | return np.log(c) + self.ψ * np.log(1 - n)
19 |
20 | # Derivatives of utility function
21 | def Uc(self, c, n):
22 | return 1 / c
23 |
24 | def Ucc(self, c, n):
25 | return -c**(-2)
26 |
27 | def Un(self, c, n):
28 | return -self.ψ / (1 - n)
29 |
30 | def Unn(self, c, n):
31 | return -self.ψ / (1 - n)**2
--------------------------------------------------------------------------------
/lectures/_static/lecture_specific/orth_proj/orth_proj_def1.tex:
--------------------------------------------------------------------------------
1 | \documentclass[convert={density=300,size=1080x800,outext=.png}]{standalone}
2 | \usepackage{tikz}
3 |
4 | \usetikzlibrary{arrows.meta, arrows}
5 |
6 | \begin{document}
7 |
8 | %.. tikz::
9 | \begin{tikzpicture}
10 | [scale=5, axis/.style={<->, >=stealth'}, important line/.style={thick}, dotted line/.style={dotted, thick,red}, every node/.style={color=black}]\coordinate(O) at (0,0);
11 | \coordinate (X) at (-0.2,0.3);
12 | \coordinate (Z) at (0.6,0.3);
13 | \draw[axis] (-0.4,0) -- (0.9,0) node(xline)[right] {};
14 | \draw[axis] (0,-0.3) -- (0,0.7) node(yline)[above] {};
15 | \draw[important line,blue, ->] (O) -- (X) node[left] {$x$};
16 | \draw[important line,blue, ->] (O) -- (Z) node[right] {$z$};
17 | \draw[dotted line] (-0.03,0.045) -- (0.03,0.075);
18 | \draw[dotted line] (0.06,0.03) -- (0.03,0.075);
19 |
20 | \end{tikzpicture}
21 |
22 | \end{document}
--------------------------------------------------------------------------------
/lectures/_static/lecture_specific/orth_proj/orth_proj_def2.tex:
--------------------------------------------------------------------------------
1 | \documentclass[convert={density=300,size=1080x800,outext=.png}]{standalone}
2 | \usepackage{tikz}
3 | \usetikzlibrary{arrows.meta, arrows}
4 | \begin{document}
5 |
6 | %.. tikz::
7 | \begin{tikzpicture}
8 | [scale=5, axis/.style={<->, >=stealth'}, important line/.style={thick}, dotted line/.style={dotted, thick,red}, every node/.style={color=black} ] \coordinate(O) at (0,0);
9 | \coordinate (X) at (-0.2,0.3);
10 | \coordinate (Z1) at (-0.3,-0.15);
11 | \coordinate (Z2) at (0.8,0.4);
12 | \draw[axis] (-0.4,0) -- (0.9,0) node(xline)[right] {};
13 | \draw[axis] (0,-0.3) -- (0,0.7) node(yline)[above] {};
14 | \draw[important line,blue, ->] (O) -- (X) node[left] {$x$};
15 | \draw[important line] (Z1) -- (Z2) node[right] {$S$};
16 | \draw[dotted line] (-0.03,0.045) -- (0.03,0.075);
17 | \draw[dotted line] (0.06,0.03) -- (0.03,0.075);
18 | \end{tikzpicture}
19 |
20 | \end{document}
21 |
22 |
23 |
--------------------------------------------------------------------------------
/lectures/_static/lecture_specific/markov_perf/duopoly_mpe.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import quantecon as qe
3 |
4 | # Parameters
5 | a0 = 10.0
6 | a1 = 2.0
7 | β = 0.96
8 | γ = 12.0
9 |
10 | # In LQ form
11 | A = np.eye(3)
12 | B1 = np.array([[0.], [1.], [0.]])
13 | B2 = np.array([[0.], [0.], [1.]])
14 |
15 |
16 | R1 = [[ 0., -a0 / 2, 0.],
17 | [-a0 / 2., a1, a1 / 2.],
18 | [ 0, a1 / 2., 0.]]
19 |
20 | R2 = [[ 0., 0., -a0 / 2],
21 | [ 0., 0., a1 / 2.],
22 | [-a0 / 2, a1 / 2., a1]]
23 |
24 | Q1 = Q2 = γ
25 | S1 = S2 = W1 = W2 = M1 = M2 = 0.0
26 |
27 | # Solve using QE's nnash function
28 | F1, F2, P1, P2 = qe.nnash(A, B1, B2, R1, R2, Q1,
29 | Q2, S1, S2, W1, W2, M1,
30 | M2, beta=β)
31 |
32 | # Display policies
33 | print("Computed policies for firm 1 and firm 2:\n")
34 | print(f"F1 = {F1}")
35 | print(f"F2 = {F2}")
36 | print("\n")
37 |
--------------------------------------------------------------------------------
/lectures/_static/lecture_specific/orth_proj/orth_proj_def3.tex:
--------------------------------------------------------------------------------
1 | \documentclass[convert={density=300,size=1080x800,outext=.png}]{standalone}
2 | \usepackage{tikz}
3 | \usetikzlibrary{arrows.meta, arrows}
4 | \begin{document}
5 |
6 | %.. tikz::
7 | \begin{tikzpicture}
8 | [scale=5, axis/.style={<->, >=stealth'}, important line/.style={thick}, dotted line/.style={dotted, thick,red}, dashed line/.style={dashed, thin}, every node/.style={color=black}] \coordinate(O) at (0,0);
9 | \coordinate (S1) at (-0.4,-0.2);
10 | \coordinate (S2) at (0.8,0.4);
11 | \coordinate (S3) at (-0.25,0.5);
12 | \coordinate (S4) at (0.12,-0.24);
13 | \draw[axis] (-0.5,0) -- (0.9,0) node(xline)[right] {};
14 | \draw[axis] (0,-0.3) -- (0,0.7) node(yline)[above] {};
15 | \draw[important line, thick] (S1) -- (S2) node[right] {$S$};
16 | \draw[important line, thick] (S4) -- (S3) node[left] {$S^{\perp}$};
17 | \draw[dotted line] (-0.03,0.06) -- (0.03,0.09);
18 | \draw[dotted line] (0.06,0.03) -- (0.03,0.09);
19 | \end{tikzpicture}
20 |
21 | \end{document}
--------------------------------------------------------------------------------
/lectures/_static/lecture_specific/amss2/crra_utility.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 |
3 |
4 | class CRRAutility:
5 |
6 | def __init__(self,
7 | β=0.9,
8 | σ=2,
9 | γ=2,
10 | π=np.full((2, 2), 0.5),
11 | G=np.array([0.1, 0.2]),
12 | Θ=np.ones(2),
13 | transfers=False):
14 |
15 | self.β, self.σ, self.γ = β, σ, γ
16 | self.π, self.G, self.Θ, self.transfers = π, G, Θ, transfers
17 |
18 | # Utility function
19 | def U(self, c, n):
20 | σ = self.σ
21 | if σ == 1.:
22 | U = np.log(c)
23 | else:
24 | U = (c**(1 - σ) - 1) / (1 - σ)
25 | return U - n**(1 + self.γ) / (1 + self.γ)
26 |
27 | # Derivatives of utility function
28 | def Uc(self, c, n):
29 | return c**(-self.σ)
30 |
31 | def Ucc(self, c, n):
32 | return -self.σ * c**(-self.σ - 1)
33 |
34 | def Un(self, c, n):
35 | return -n**self.γ
36 |
37 | def Unn(self, c, n):
38 | return -self.γ * n**(self.γ - 1)
--------------------------------------------------------------------------------
/.github/workflows/linkcheck.yml:
--------------------------------------------------------------------------------
1 | name: Link Checker [Anaconda, Linux]
2 | on:
3 | schedule:
4 | # UTC 23:00 is early morning in Australia (9am) every Sunday
5 | - cron: '0 23 * * 0'
6 | workflow_dispatch:
7 | jobs:
8 | link-checking:
9 | name: Link Checking
10 | runs-on: "ubuntu-latest"
11 | permissions:
12 | issues: write # required for peter-evans/create-issue-from-file
13 | steps:
14 | # Checkout the live site (html)
15 | - name: Checkout
16 | uses: actions/checkout@v5
17 | with:
18 | ref: gh-pages
19 | - name: Link Checker
20 | id: lychee
21 | uses: lycheeverse/lychee-action@v2
22 | with:
23 | fail: false
24 | # Configuration is now specified in lychee.toml file
25 | args: **/*.html
26 | - name: Create Issue From File
27 | if: steps.lychee.outputs.exit_code != 0
28 | uses: peter-evans/create-issue-from-file@v5
29 | with:
30 | title: Link Checker Report
31 | content-filepath: ./lychee/out.md
32 | labels: report, automated issue, linkchecker
--------------------------------------------------------------------------------
/lectures/_static/lecture_specific/opt_tax_recur/crra_utility.py:
--------------------------------------------------------------------------------
1 | crra_util_data = [
2 | ('β', float64),
3 | ('σ', float64),
4 | ('γ', float64)
5 | ]
6 |
7 | @jitclass(crra_util_data)
8 | class CRRAutility:
9 |
10 | def __init__(self,
11 | β=0.9,
12 | σ=2,
13 | γ=2):
14 |
15 | self.β, self.σ, self.γ = β, σ, γ
16 |
17 | # Utility function
18 | def U(self, c, l):
19 | # Note: `l` should not be interpreted as labor, it is an auxiliary
20 | # variable used to conveniently match the code and the equations
21 | # in the lecture
22 | σ = self.σ
23 | if σ == 1.:
24 | U = np.log(c)
25 | else:
26 | U = (c**(1 - σ) - 1) / (1 - σ)
27 | return U - (1-l) ** (1 + self.γ) / (1 + self.γ)
28 |
29 | # Derivatives of utility function
30 | def Uc(self, c, l):
31 | return c ** (-self.σ)
32 |
33 | def Ucc(self, c, l):
34 | return -self.σ * c ** (-self.σ - 1)
35 |
36 | def Ul(self, c, l):
37 | return (1-l) ** self.γ
38 |
39 | def Ull(self, c, l):
40 | return -self.γ * (1-l) ** (self.γ - 1)
41 |
42 | def Ucl(self, c, l):
43 | return 0
44 |
45 | def Ulc(self, c, l):
46 | return 0
47 |
--------------------------------------------------------------------------------
/lectures/_static/lecture_specific/orth_proj/orth_proj_thm1.tex:
--------------------------------------------------------------------------------
1 | \documentclass[convert={density=300,size=1080x800,outext=.png}]{standalone}
2 | \usepackage{tikz}
3 | \usetikzlibrary{arrows.meta, arrows}
4 | \begin{document}
5 |
6 | %.. tikz::
7 | \begin{tikzpicture}
8 | [scale=5, axis/.style={<->, >=stealth'}, important line/.style={thick}, dotted line/.style={dotted, thick,red}, dashed line/.style={dashed, thin}, every node/.style={color=black}] \coordinate(O) at (0,0);
9 | \coordinate (y-yhat) at (-0.2,0.4);
10 | \coordinate (yhat) at (0.6,0.3);
11 | \coordinate (y) at (0.4,0.7);
12 | \coordinate (Z1) at (-0.4,-0.2);
13 | \coordinate (Z2) at (0.8,0.4);
14 | \draw[axis] (-0.5,0) -- (0.9,0) node(xline)[right] {};
15 | \draw[axis] (0,-0.3) -- (0,0.7) node(yline)[above] {};
16 | \draw[important line,blue,thick, ->] (O) -- (yhat) node[below] {$\hat y$};
17 | \draw[important line,blue, ->] (O) -- (y-yhat) node[left] {$y - \hat y$};
18 | \draw[important line, thick] (Z1) -- (O) node[right] {};
19 | \draw[important line, thick] (yhat) -- (Z2) node[right] {$S$};
20 | \draw[important line, blue,->] (O) -- (y) node[right] {$y$};
21 | \draw[dotted line] (-0.03,0.06) -- (0.03,0.09);
22 | \draw[dotted line] (0.06,0.03) -- (0.03,0.09);
23 | \draw[dotted line] (0.54,0.27) -- (0.51,0.33);
24 | \draw[dotted line] (0.57,0.36) -- (0.51,0.33);
25 | \draw[dashed line, black] (y) -- (yhat);
26 | \draw[-latex, very thin] (0.5,0.4) to [out=210,in=50] (-0.1,0.2);
27 |
28 | \end{tikzpicture}
29 |
30 | \end{document}
--------------------------------------------------------------------------------
/lectures/_static/lecture_specific/orth_proj/orth_proj_thm2.tex:
--------------------------------------------------------------------------------
1 | \documentclass[convert={density=300,size=1080x800,outext=.png}]{standalone}
2 | \usepackage{tikz}
3 | \usetikzlibrary{arrows.meta, arrows}
4 | \begin{document}
5 |
6 | %.. tikz::
7 | \begin{tikzpicture}
8 | [scale=5, axis/.style={<->, >=stealth'}, important line/.style={thick}, dotted line/.style={dotted, thick,red}, dashed line/.style={dashed, thin}, every node/.style={color=black}] \coordinate(O) at (0,0);
9 | \coordinate (y') at (-0.4,0.1);
10 | \coordinate (Py) at (0.6,0.3);
11 | \coordinate (y) at (0.4,0.7);
12 | \coordinate (Z1) at (-0.4,-0.2);
13 | \coordinate (Z2) at (0.8,0.4);
14 | \coordinate (Py') at (-0.28,-0.14);
15 | \draw[axis] (-0.5,0) -- (0.9,0) node(xline)[right] {};
16 | \draw[axis] (0,-0.3) -- (0,0.7) node(yline)[above] {};
17 | \draw[important line,blue,thick, ->] (O) -- (Py) node[anchor = north west, text width=2em] {$P y$};
18 | \draw[important line,blue, ->] (O) -- (y') node[left] {$y'$};
19 | \draw[important line, thick] (Z1) -- (O) node[right] {};
20 | \draw[important line, thick] (Py) -- (Z2) node[right] {$S$};
21 | \draw[important line, blue,->] (O) -- (y) node[right] {$y$};
22 | \draw[important line, blue,->] (O) -- (Py') node[anchor = north west, text width=5em] {$P y'$};
23 | \draw[dotted line] (0.54,0.27) -- (0.51,0.33);
24 | \draw[dotted line] (0.57,0.36) -- (0.51,0.33);
25 | \draw[dotted line] (-0.22,-0.11) -- (-0.25,-0.05);
26 | \draw[dotted line] (-0.31,-0.08) -- (-0.25,-0.05);
27 | \draw[dashed line, black] (y) -- (Py);
28 | \draw[dashed line, black] (y') -- (Py');
29 | \end{tikzpicture}
30 |
31 | \end{document}
--------------------------------------------------------------------------------
/.github/workflows/cache.yml:
--------------------------------------------------------------------------------
1 | name: Build Cache [using jupyter-book]
2 | on:
3 | schedule:
4 | # Execute cache weekly at 3am on Monday
5 | - cron: '0 3 * * 1'
6 | workflow_dispatch:
7 | jobs:
8 | tests:
9 | runs-on: ubuntu-latest
10 | steps:
11 | - name: Checkout
12 | uses: actions/checkout@v5
13 | - name: Setup Anaconda
14 | uses: conda-incubator/setup-miniconda@v3
15 | with:
16 | auto-update-conda: true
17 | auto-activate-base: true
18 | miniconda-version: 'latest'
19 | python-version: "3.13"
20 | environment-file: environment.yml
21 | activate-environment: quantecon
22 | - name: Install latex dependencies
23 | run: |
24 | sudo apt-get -qq update
25 | sudo apt-get install -y \
26 | texlive-latex-recommended \
27 | texlive-latex-extra \
28 | texlive-fonts-recommended \
29 | texlive-fonts-extra \
30 | texlive-xetex \
31 | latexmk \
32 | xindy \
33 | dvipng \
34 | cm-super
35 | - name: Build HTML
36 | shell: bash -l {0}
37 | run: |
38 | jb build lectures --path-output ./ -W --keep-going
39 | - name: Upload Execution Reports
40 | uses: actions/upload-artifact@v4
41 | if: failure()
42 | with:
43 | name: execution-reports
44 | path: _build/html/reports
45 | - name: Upload "_build" folder (cache)
46 | uses: actions/upload-artifact@v4
47 | with:
48 | name: build-cache
49 | path: _build
50 | include-hidden-files: true
--------------------------------------------------------------------------------
/lectures/_static/lecture_specific/orth_proj/orth_proj_thm3.tex:
--------------------------------------------------------------------------------
1 | \documentclass[convert={density=300,size=1080x800,outext=.png}]{standalone}
2 | \usepackage{tikz}
3 | \usetikzlibrary{arrows.meta, arrows}
4 | \begin{document}
5 |
6 | %.. tikz::
7 | \begin{tikzpicture}
8 | [scale=5, axis/.style={<->, >=stealth'}, important line/.style={thick}, dotted line/.style={dotted, thick,red}, dashed line/.style={dashed, thin}, every node/.style={color=black}] \coordinate(O) at (0,0);
9 | \coordinate (uhat) at (-0.2,0.4);
10 | \coordinate (yhat) at (0.6,0.3);
11 | \coordinate (y) at (0.4,0.7);
12 | \coordinate (S1) at (-0.4,-0.2);
13 | \coordinate (S2) at (0.8,0.4);
14 | \coordinate (S3) at (-0.3,0.6);
15 | \coordinate (S4) at (0.12,-0.24);
16 | \draw[axis] (-0.5,0) -- (0.9,0) node(xline)[right] {};
17 | \draw[axis] (0,-0.3) -- (0,0.7) node(yline)[above] {};
18 | \draw[important line,blue,thick, ->] (O) -- (yhat) node[anchor = north west, text width=4em] {$P y$};
19 | \draw[important line,blue, ->] (O) -- (uhat) node[anchor = north east, text width=4em] {$M y$};
20 | \draw[important line,thick] (uhat) -- (S3) node [anchor = south east, text width=0.5em] {$S^{\perp}$};
21 | \draw[important line,thick] (O) -- (S4);
22 | \draw[important line, thick] (S1) -- (O) node[right] {};
23 | \draw[important line, thick] (yhat) -- (S2) node[right] {$S$};
24 | \draw[important line, blue,->] (O) -- (y) node[right] {$y$};
25 | \draw[dotted line] (-0.03,0.06) -- (0.03,0.09);
26 | \draw[dotted line] (0.06,0.03) -- (0.03,0.09);
27 | \draw[dotted line] (0.54,0.27) -- (0.51,0.33);
28 | \draw[dotted line] (0.57,0.36) -- (0.51,0.33);
29 | \draw[dotted line] (-0.17,0.34) -- (-0.11,0.37);
30 | \draw[dotted line] (-0.14,0.43) -- (-0.11,0.37);
31 | \draw[dashed line, black] (y) -- (yhat);
32 | \draw[dashed line, black] (y) -- (uhat);
33 | \end{tikzpicture}
34 |
35 | \end{document}
--------------------------------------------------------------------------------
/lectures/_static/lecture_specific/amss/utilities.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | from scipy.interpolate import UnivariateSpline
3 |
4 |
5 | class interpolate_wrapper:
6 |
7 | def __init__(self, F):
8 | self.F = F
9 |
10 | def __getitem__(self, index):
11 | return interpolate_wrapper(np.asarray(self.F[index]))
12 |
13 | def reshape(self, *args):
14 | self.F = self.F.reshape(*args)
15 | return self
16 |
17 | def transpose(self):
18 | self.F = self.F.transpose()
19 |
20 | def __len__(self):
21 | return len(self.F)
22 |
23 | def __call__(self, xvec):
24 | x = np.atleast_1d(xvec)
25 | shape = self.F.shape
26 | if len(x) == 1:
27 | fhat = np.hstack([f(x) for f in self.F.flatten()])
28 | return fhat.reshape(shape)
29 | else:
30 | fhat = np.vstack([f(x) for f in self.F.flatten()])
31 | return fhat.reshape(np.hstack((shape, len(x))))
32 |
33 |
34 | class interpolator_factory:
35 |
36 | def __init__(self, k, s):
37 | self.k, self.s = k, s
38 |
39 | def __call__(self, xgrid, Fs):
40 | shape, m = Fs.shape[:-1], Fs.shape[-1]
41 | Fs = Fs.reshape((-1, m))
42 | F = []
43 | xgrid = np.sort(xgrid) # Sort xgrid
44 | for Fhat in Fs:
45 | F.append(UnivariateSpline(xgrid, Fhat, k=self.k, s=self.s))
46 | return interpolate_wrapper(np.array(F).reshape(shape))
47 |
48 |
49 | def fun_vstack(fun_list):
50 |
51 | Fs = [IW.F for IW in fun_list]
52 | return interpolate_wrapper(np.vstack(Fs))
53 |
54 |
55 | def fun_hstack(fun_list):
56 |
57 | Fs = [IW.F for IW in fun_list]
58 | return interpolate_wrapper(np.hstack(Fs))
59 |
60 |
61 | def simulate_markov(π, s_0, T):
62 |
63 | sHist = np.empty(T, dtype=int)
64 | sHist[0] = s_0
65 | S = len(π)
66 | for t in range(1, T):
67 | sHist[t] = np.random.choice(np.arange(S), p=π[sHist[t - 1]])
68 |
69 | return sHist
70 |
--------------------------------------------------------------------------------
/lectures/_static/lecture_specific/amss2/utilities.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | from scipy.interpolate import UnivariateSpline
3 |
4 |
5 | class interpolate_wrapper:
6 |
7 | def __init__(self, F):
8 | self.F = F
9 |
10 | def __getitem__(self, index):
11 | return interpolate_wrapper(np.asarray(self.F[index]))
12 |
13 | def reshape(self, *args):
14 | self.F = self.F.reshape(*args)
15 | return self
16 |
17 | def transpose(self):
18 | self.F = self.F.transpose()
19 |
20 | def __len__(self):
21 | return len(self.F)
22 |
23 | def __call__(self, xvec):
24 | x = np.atleast_1d(xvec)
25 | shape = self.F.shape
26 | if len(x) == 1:
27 | fhat = np.hstack([f(x) for f in self.F.flatten()])
28 | return fhat.reshape(shape)
29 | else:
30 | fhat = np.vstack([f(x) for f in self.F.flatten()])
31 | return fhat.reshape(np.hstack((shape, len(x))))
32 |
33 |
34 | class interpolator_factory:
35 |
36 | def __init__(self, k, s):
37 | self.k, self.s = k, s
38 |
39 | def __call__(self, xgrid, Fs):
40 | shape, m = Fs.shape[:-1], Fs.shape[-1]
41 | Fs = Fs.reshape((-1, m))
42 | F = []
43 | xgrid = np.sort(xgrid) # Sort xgrid
44 | for Fhat in Fs:
45 | F.append(UnivariateSpline(xgrid, Fhat, k=self.k, s=self.s))
46 | return interpolate_wrapper(np.array(F).reshape(shape))
47 |
48 |
49 | def fun_vstack(fun_list):
50 |
51 | Fs = [IW.F for IW in fun_list]
52 | return interpolate_wrapper(np.vstack(Fs))
53 |
54 |
55 | def fun_hstack(fun_list):
56 |
57 | Fs = [IW.F for IW in fun_list]
58 | return interpolate_wrapper(np.hstack(Fs))
59 |
60 |
61 | def simulate_markov(π, s_0, T):
62 |
63 | sHist = np.empty(T, dtype=int)
64 | sHist[0] = s_0
65 | S = len(π)
66 | for t in range(1, T):
67 | sHist[t] = np.random.choice(np.arange(S), p=π[sHist[t - 1]])
68 |
69 | return sHist
70 |
--------------------------------------------------------------------------------
/lectures/_toc.yml:
--------------------------------------------------------------------------------
1 | format: jb-book
2 | root: intro
3 | parts:
4 | - caption: Tools and Techniques
5 | numbered: true
6 | chapters:
7 | - file: orth_proj
8 | - file: stationary_densities
9 | - file: muth_kalman
10 | - file: discrete_dp
11 | - caption: LQ Control
12 | numbered: true
13 | chapters:
14 | - file: cons_news
15 | - file: smoothing
16 | - file: smoothing_tax
17 | - file: markov_jump_lq
18 | - file: tax_smoothing_1
19 | - file: tax_smoothing_2
20 | - file: tax_smoothing_3
21 | - file: lqramsey
22 | - caption: Multiple Agent Models
23 | numbered: true
24 | chapters:
25 | - file: arellano
26 | - file: matsuyama
27 | - file: coase
28 | - file: match_transport
29 | - caption: Dynamic Linear Economies
30 | numbered: true
31 | chapters:
32 | - file: hs_recursive_models
33 | - file: growth_in_dles
34 | - file: lucas_asset_pricing_dles
35 | - file: irfs_in_hall_model
36 | - file: permanent_income_dles
37 | - file: rosen_schooling_model
38 | - file: cattle_cycles
39 | - file: hs_invertibility_example
40 | - caption: Risk, Model Uncertainty, and Robustness
41 | numbered: true
42 | chapters:
43 | - file: five_preferences
44 | - file: entropy
45 | - file: robustness
46 | - file: rob_markov_perf
47 | - caption: Time Series Models
48 | numbered: true
49 | chapters:
50 | - file: arma
51 | - file: estspec
52 | - file: additive_functionals
53 | - file: lu_tricks
54 | - file: classical_filtering
55 | - file: knowing_forecasts_of_others
56 | - caption: Asset Pricing and Finance
57 | numbered: true
58 | chapters:
59 | - file: lucas_model
60 | - file: asset_pricing_lph
61 | - file: black_litterman
62 | - file: BCG_complete_mkts
63 | - file: BCG_incomplete_mkts
64 | - caption: Dynamic Programming Squared
65 | numbered: true
66 | chapters:
67 | - file: un_insure
68 | - file: dyn_stack
69 | - file: calvo_machine_learn
70 | - file: calvo
71 | - file: calvo_abreu
72 | - file: opt_tax_recur
73 | - file: amss
74 | - file: amss2
75 | - file: amss3
76 | - file: chang_ramsey
77 | - file: chang_credible
78 | - caption: Other
79 | numbered: true
80 | chapters:
81 | - file: troubleshooting
82 | - file: zreferences
83 | - file: status
84 |
--------------------------------------------------------------------------------
/lectures/_static/lecture_specific/coase/allocation.tex:
--------------------------------------------------------------------------------
1 | \documentclass[convert={density=300,size=1080x800,outext=.png}]{standalone}
2 |
3 | \usepackage{tikz}
4 | \usetikzlibrary{decorations.pathreplacing}
5 | \usetikzlibrary{arrows,positioning}
6 | \tikzset{
7 | %Define standard arrow tip
8 | >=stealth',
9 | %Define style for boxes
10 | punkt/.style={
11 | rectangle,
12 | rounded corners,
13 | draw=black, very thick,
14 | text width=6.5em,
15 | minimum height=2em,
16 | text centered},
17 | % Define arrow style
18 | pil/.style={
19 | ->,
20 | thick}
21 | }
22 |
23 |
24 | \begin{document}
25 |
26 | \begin{tikzpicture}[scale=1]
27 |
28 | \def\linelen{12}
29 | \def\tone{0.66 * \linelen};
30 | \def\ttwo{0.33 * \linelen};
31 |
32 | \draw[thick] (0,0) -- (\linelen,0);
33 |
34 | \fill (0,0) circle (2pt) node [below] {$t_3 = 0$};
35 | \fill (\linelen,0) circle (2pt) node [below] {$t_0 = 1$};
36 | \fill (\tone,0) circle (2pt) node [below] {$t_1$};
37 | \fill (\ttwo,0) circle (2pt) node [below] {$t_2$};
38 |
39 | \draw [decorate,decoration={brace,amplitude=10pt},xshift=0pt,yshift=3pt]
40 | (0,0) -- (\ttwo,0) node [black,midway,yshift=0.8cm] {\footnotesize
41 | $\ell_3$};
42 |
43 | \draw [decorate,decoration={brace,amplitude=10pt},xshift=0pt,yshift=3pt]
44 | (\ttwo,0) -- (\tone,0) node [black,midway,yshift=0.8cm] {\footnotesize
45 | $\ell_2$};
46 |
47 | \draw [decorate,decoration={brace,amplitude=10pt},xshift=0pt,yshift=3pt]
48 | (\tone,0) -- (\linelen,0) node [black,midway,yshift=0.8cm] {\footnotesize
49 | $\ell_1$};
50 |
51 | \node at (10, 0) [below] {\footnotesize firm 1};
52 | \node at (6, 0) [below] {\footnotesize firm 2};
53 | \node at (2, 0) [below] {\footnotesize firm 3};
54 |
55 | % Notation on in-house production and firm boundaries
56 | \def\a{2};
57 | \draw[->] (5.9, \a) -- (5.9, 1.15) ;
58 | \draw[->] (6, \a) node [above] {\footnotesize $\ell_i = $ range of tasks
59 | carried out by firm $i$} -- (9.5, 1.1) ;
60 | \draw[->] (5.8, \a) -- (2.5, 1.1) ;
61 |
62 | \def\b{-2};
63 | \draw[->] (6, \b) -- (\tone, -0.8) ;
64 | \draw[->] (5.9, \b) node [below] {\footnotesize $t_i = $ upstream boundary
65 | of firm $i$} -- (\ttwo, -0.8) ;
66 | \draw[->] (5.8, \b) -- (0, -0.8) ;
67 |
68 | \end{tikzpicture}
69 |
70 |
71 | \end{document}
72 |
--------------------------------------------------------------------------------
/lectures/troubleshooting.md:
--------------------------------------------------------------------------------
1 | ---
2 | jupytext:
3 | text_representation:
4 | extension: .md
5 | format_name: myst
6 | kernelspec:
7 | display_name: Python 3
8 | language: python
9 | name: python3
10 | ---
11 |
12 | (troubleshooting)=
13 | ```{raw} html
14 |
19 | ```
20 |
21 | # Troubleshooting
22 |
23 | This page is for readers experiencing errors when running the code from the lectures.
24 |
25 | ## Fixing your local environment
26 |
27 | The basic assumption of the lectures is that code in a lecture should execute whenever
28 |
29 | 1. it is executed in a Jupyter notebook and
30 | 1. the notebook is running on a machine with the latest version of Anaconda Python.
31 |
32 | You have installed Anaconda, haven't you, following the instructions in [this lecture](https://python-programming.quantecon.org/getting_started.html)?
33 |
34 | Assuming that you have, the most common source of problems for our readers is that their Anaconda distribution is not up to date.
35 |
36 | [Here's a useful article](https://www.anaconda.com/keeping-anaconda-date/)
37 | on how to update Anaconda.
38 |
39 | Another option is to simply remove Anaconda and reinstall.
40 |
41 | You also need to keep the external code libraries, such as [QuantEcon.py](https://quantecon.org/quantecon-py) up to date.
42 |
43 | For this task you can either
44 |
45 | * use pip install --upgrade quantecon on the command line, or
46 | * execute !pip install --upgrade quantecon within a Jupyter notebook.
47 |
48 | If your local environment is still not working you can do two things.
49 |
50 | First, you can use a remote machine instead, by clicking on the Launch Notebook icon available for each lecture
51 |
52 | ```{image} _static/lecture_specific/troubleshooting/launch.png
53 |
54 | ```
55 |
56 | Second, you can report an issue, so we can try to fix your local set up.
57 |
58 | We like getting feedback on the lectures so please don't hesitate to get in
59 | touch.
60 |
61 | ## Reporting an issue
62 |
63 | One way to give feedback is to raise an issue through our [issue tracker](https://github.com/QuantEcon/lecture-python-advanced/issues).
64 |
65 | Please be as specific as possible. Tell us where the problem is and as much
66 | detail about your local set up as you can provide.
67 |
68 | Finally, you can provide direct feedback to [contact@quantecon.org](mailto:contact@quantecon.org)
69 |
70 |
--------------------------------------------------------------------------------
/lectures/_static/lecture_specific/coase/subcontracting.tex:
--------------------------------------------------------------------------------
1 | \documentclass[convert={density=300,size=1080x800,outext=.png}]{standalone}
2 |
3 | \usepackage{tikz}
4 | \usetikzlibrary{decorations.pathreplacing}
5 | \usetikzlibrary{arrows,positioning}
6 | \tikzset{
7 | %Define standard arrow tip
8 | >=stealth',
9 | %Define style for boxes
10 | punkt/.style={
11 | rectangle,
12 | rounded corners,
13 | draw=black, very thick,
14 | text width=6.5em,
15 | minimum height=2em,
16 | text centered},
17 | % Define arrow style
18 | pil/.style={
19 | ->,
20 | thick}
21 | }
22 |
23 |
24 | \begin{document}
25 |
26 | \begin{tikzpicture}[scale=1]
27 |
28 | \def\linelen{12}
29 | \def\tone{0.66 * \linelen};
30 | \def\ttwo{0.33 * \linelen};
31 | \def\levtwo{-2.2}; % level 2
32 | \def\levthree{-4.2}; % level 2
33 |
34 | \draw[thick] (0,0) -- (\linelen,0);
35 |
36 | \node at (0,0) [below] {$0$};
37 | \node at (\linelen,0) [below] {$1$};
38 | \fill (\tone,0) circle (2pt) node [below] {$t_1$};
39 |
40 | \draw [decorate,decoration={brace,amplitude=10pt},xshift=0pt,yshift=3pt]
41 | (\tone,0) -- (\linelen,0) node [black,midway,yshift=0.8cm] {\footnotesize
42 | processed by firm 1};
43 | \draw [decorate,decoration={brace,amplitude=10pt},xshift=0pt,yshift=3pt]
44 | (0,0) -- (\tone,0) node [black,midway,yshift=0.8cm] {\footnotesize
45 | subcontracted by firm 1 to firm 2};
46 |
47 | \draw[->] (0.5 * \tone, -0.2) -- (0.5 * \tone, -0.9);
48 |
49 | \draw[thick] (0, \levtwo) -- (\tone, \levtwo);
50 | \node at (0,\levtwo) [below] {$0$};
51 | \fill (\tone,\levtwo) circle (2pt) node [below] {$t_1$};
52 | \fill (\ttwo,\levtwo) circle (2pt) node [below] {$t_2$};
53 |
54 | \draw [decorate,decoration={brace,amplitude=10pt},xshift=0pt,yshift=3pt]
55 | (0,\levtwo) -- (\ttwo,\levtwo) node [black,midway,yshift=0.8cm] {\footnotesize
56 | subcontracted by 2 to 3};
57 | \draw [decorate,decoration={brace,amplitude=10pt},xshift=0pt,yshift=3pt]
58 | (\ttwo,\levtwo) -- (\tone,\levtwo) node [black,midway,yshift=0.8cm] {\footnotesize
59 | processed by firm 2};
60 |
61 | \draw[->] (0.5 * \ttwo, -2.4) -- (0.5 * \ttwo, -2.9);
62 |
63 | \draw[thick] (0, \levthree) -- (\ttwo, \levthree);
64 | \node at (0,\levthree) [below] {$0$};
65 | \fill (\ttwo,\levthree) circle (2pt) node [below] {$t_2$};
66 |
67 | \draw [decorate,decoration={brace,amplitude=10pt},xshift=0pt,yshift=3pt]
68 | (0,\levthree) -- (\ttwo,\levthree) node [black,midway,yshift=0.8cm] {\footnotesize
69 | processed by firm 3};
70 |
71 | \end{tikzpicture}
72 |
73 | \end{document}
74 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI - Build HTML [using jupyter-book]
2 | on:
3 | pull_request:
4 | workflow_dispatch:
5 | jobs:
6 | tests:
7 | runs-on: ubuntu-latest
8 | steps:
9 | - name: Checkout
10 | uses: actions/checkout@v5
11 | - name: Setup Anaconda
12 | uses: conda-incubator/setup-miniconda@v3
13 | with:
14 | auto-update-conda: true
15 | auto-activate-base: true
16 | miniconda-version: 'latest'
17 | python-version: "3.13"
18 | environment-file: environment.yml
19 | activate-environment: quantecon
20 | - name: Install latex dependencies
21 | run: |
22 | sudo apt-get -qq update
23 | sudo apt-get install -y \
24 | texlive-latex-recommended \
25 | texlive-latex-extra \
26 | texlive-fonts-recommended \
27 | texlive-fonts-extra \
28 | texlive-xetex \
29 | latexmk \
30 | xindy \
31 | dvipng \
32 | cm-super
33 | - name: Display Conda Environment Versions
34 | shell: bash -l {0}
35 | run: conda list
36 | - name: Display Pip Versions
37 | shell: bash -l {0}
38 | run: pip list
39 | - name: Download "build" folder (cache)
40 | uses: dawidd6/action-download-artifact@v11
41 | with:
42 | workflow: cache.yml
43 | branch: main
44 | name: build-cache
45 | path: _build
46 | # Build Assets (Download Notebooks and PDF via LaTeX)
47 | - name: Build PDF from LaTeX
48 | shell: bash -l {0}
49 | run: |
50 | jb build lectures --builder pdflatex --path-output ./ -n --keep-going
51 | mkdir -p _build/html/_pdf
52 | cp -u _build/latex/*.pdf _build/html/_pdf
53 | - name: Build Download Notebooks (sphinx-tojupyter)
54 | shell: bash -l {0}
55 | run: |
56 | jb build lectures --path-output ./ --builder=custom --custom-builder=jupyter
57 | mkdir -p _build/html/_notebooks
58 | cp -u _build/jupyter/*.ipynb _build/html/_notebooks
59 | # Build HTML (Website)
60 | # BUG: rm .doctress to remove `sphinx` rendering issues for ipywidget mimetypes
61 | # and clear the sphinx cache for building final HTML documents.
62 | - name: Build HTML
63 | shell: bash -l {0}
64 | run: |
65 | rm -r _build/.doctrees
66 | jb build lectures --path-output ./ -nW --keep-going
67 | - name: Upload Execution Reports (Download Notebooks)
68 | uses: actions/upload-artifact@v4
69 | if: failure()
70 | with:
71 | name: execution-reports
72 | path: _build/jupyter/reports
73 | - name: Save Build as Artifact
74 | uses: actions/upload-artifact@v4
75 | with:
76 | name: _build
77 | path: _build
78 | - name: Preview Deploy to Netlify
79 | uses: nwtgck/actions-netlify@v3
80 | with:
81 | publish-dir: '_build/html/'
82 | production-branch: main
83 | github-token: ${{ secrets.GITHUB_TOKEN }}
84 | deploy-message: "Preview Deploy from GitHub Actions"
85 | env:
86 | NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
87 | NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}
--------------------------------------------------------------------------------
/lectures/_static/lecture_specific/lucas_model/lucastree.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | from scipy.stats import lognorm
3 | from scipy.integrate import fixed_quad
4 |
5 |
6 | class LucasTree:
7 | """
8 | Class to store parameters of a the Lucas tree model, a grid for the
9 | iteration step and some other helpful bits and pieces.
10 |
11 | Parameters
12 | ----------
13 | γ : scalar(float)
14 | The coefficient of risk aversion in the household's CRRA utility
15 | function
16 | β : scalar(float)
17 | The household's discount factor
18 | α : scalar(float)
19 | The correlation coefficient in the shock process
20 | σ : scalar(float)
21 | The volatility of the shock process
22 | grid_size : int
23 | The size of the grid to use
24 |
25 | Attributes
26 | ----------
27 | γ, β, α, σ, grid_size : see Parameters
28 | grid : ndarray
29 | Properties for grid upon which prices are evaluated
30 | ϕ : scipy.stats.lognorm
31 | The distribution for the shock process
32 |
33 | Examples
34 | --------
35 | >>> tree = LucasTree(γ=2, β=0.95, α=0.90, σ=0.1)
36 | >>> price_vals = solve_lucas_model(tree)
37 |
38 | """
39 |
40 | def __init__(self,
41 | γ=2,
42 | β=0.95,
43 | α=0.90,
44 | σ=0.1,
45 | grid_size=100):
46 |
47 | self.γ, self.β, self.α, self.σ = γ, β, α, σ
48 |
49 | # == Set the grid interval to contain most of the mass of the
50 | # stationary distribution of the consumption endowment == #
51 | ssd = self.σ / np.sqrt(1 - self.α**2)
52 | grid_min, grid_max = np.exp(-4 * ssd), np.exp(4 * ssd)
53 | self.grid = np.linspace(grid_min, grid_max, grid_size)
54 | self.grid_size = grid_size
55 |
56 | # == set up distribution for shocks == #
57 | self.ϕ = lognorm(σ)
58 | self.draws = self.ϕ.rvs(500)
59 |
60 | # == h(y) = β * int G(y,z)^(1-γ) ϕ(dz) == #
61 | self.h = np.empty(self.grid_size)
62 | for i, y in enumerate(self.grid):
63 | self.h[i] = β * np.mean((y**α * self.draws)**(1 - γ))
64 |
65 |
66 |
67 | ## == Now the functions that act on a Lucas Tree == #
68 |
69 | def lucas_operator(f, tree, Tf=None):
70 | """
71 | The approximate Lucas operator, which computes and returns the
72 | updated function Tf on the grid points.
73 |
74 | Parameters
75 | ----------
76 | f : array_like(float)
77 | A candidate function on R_+ represented as points on a grid
78 | and should be flat NumPy array with len(f) = len(grid)
79 |
80 | tree : instance of LucasTree
81 | Stores the parameters of the problem
82 |
83 | Tf : array_like(float)
84 | Optional storage array for Tf
85 |
86 | Returns
87 | -------
88 | Tf : array_like(float)
89 | The updated function Tf
90 |
91 | Notes
92 | -----
93 | The argument `Tf` is optional, but recommended. If it is passed
94 | into this function, then we do not have to allocate any memory
95 | for the array here. As this function is often called many times
96 | in an iterative algorithm, this can save significant computation
97 | time.
98 |
99 | """
100 | grid, h = tree.grid, tree.h
101 | α, β = tree.α, tree.β
102 | z_vec = tree.draws
103 |
104 | # == turn f into a function == #
105 | Af = lambda x: np.interp(x, grid, f)
106 |
107 | # == set up storage if needed == #
108 | if Tf is None:
109 | Tf = np.empty_like(f)
110 |
111 | # == Apply the T operator to f using Monte Carlo integration == #
112 | for i, y in enumerate(grid):
113 | Tf[i] = h[i] + β * np.mean(Af(y**α * z_vec))
114 |
115 | return Tf
116 |
117 | def solve_lucas_model(tree, tol=1e-6, max_iter=500):
118 | """
119 | Compute the equilibrium price function associated with Lucas
120 | tree
121 |
122 | Parameters
123 | ----------
124 | tree : An instance of LucasTree
125 | Contains parameters
126 | tol : float
127 | error tolerance
128 | max_iter : int
129 | the maximum number of iterations
130 |
131 | Returns
132 | -------
133 | price : array_like(float)
134 | The prices at the grid points in the attribute `grid` of the object
135 |
136 | """
137 |
138 | # == simplify notation == #
139 | grid, grid_size = tree.grid, tree.grid_size
140 | γ = tree.γ
141 |
142 | # == Create storage array for lucas_operator. Reduces memory
143 | # allocation and speeds code up == #
144 | Tf = np.empty(grid_size)
145 |
146 | i = 0
147 | f = np.empty(grid_size) # Initial guess of f
148 | error = tol + 1
149 |
150 | while error > tol and i < max_iter:
151 | f_new = lucas_operator(f, tree, Tf)
152 | error = np.max(np.abs(f_new - f))
153 | f[:] = f_new
154 | i += 1
155 |
156 | price = f * grid**γ # Back out price vector
157 |
158 | return price
--------------------------------------------------------------------------------
/lectures/_config.yml:
--------------------------------------------------------------------------------
1 | title: Advanced Quantitative Economics with Python
2 | author: Thomas J. Sargent & John Stachurski
3 | logo: _static/qe-logo-large.png
4 | description: This website presents a set of lectures on advanced quantitative economic modeling, designed and written by Thomas J. Sargent and John Stachurski.
5 |
6 | parse:
7 | myst_enable_extensions:
8 | - amsmath
9 | - colon_fence
10 | - deflist
11 | - dollarmath
12 | - html_admonition
13 | - html_image
14 | - linkify
15 | - replacements
16 | - smartquotes
17 | - substitution
18 |
19 | execute:
20 | execute_notebooks: "cache"
21 | timeout: 7200 # 2 Hours
22 |
23 | bibtex_bibfiles:
24 | - _static/quant-econ.bib
25 |
26 | html:
27 | baseurl: https://python-advanced.quantecon.org/
28 |
29 | latex:
30 | latex_documents:
31 | targetname: quantecon-python-advanced.tex
32 |
33 | sphinx:
34 | extra_extensions: [sphinx_multitoc_numbering, sphinxext.rediraffe, sphinx_tojupyter, sphinx_exercise, sphinx_togglebutton, sphinx_proof]
35 | config:
36 | bibtex_reference_style: author_year
37 | linkcheck_ignore: ['https://doi.org/10.3982/ECTA8070',
38 | 'https://doi.org/10.1086/261749',
39 | 'https://doi.org/10.1086/262078',
40 | 'https://keras.io/',
41 | 'https://data.oecd.org/']
42 | nb_mime_priority_overrides: [
43 | # HTML
44 | ['html', 'application/vnd.jupyter.widget-view+json', 10],
45 | ['html', 'application/javascript', 20],
46 | ['html', 'text/html', 30],
47 | ['html', 'text/latex', 40],
48 | ['html', 'image/svg+xml', 50],
49 | ['html', 'image/png', 60],
50 | ['html', 'image/jpeg', 70],
51 | ['html', 'text/markdown', 80],
52 | ['html', 'text/plain', 90],
53 | # Jupyter Notebooks
54 | ['jupyter', 'application/vnd.jupyter.widget-view+json', 10],
55 | ['jupyter', 'application/javascript', 20],
56 | ['jupyter', 'text/html', 30],
57 | ['jupyter', 'text/latex', 40],
58 | ['jupyter', 'image/svg+xml', 50],
59 | ['jupyter', 'image/png', 60],
60 | ['jupyter', 'image/jpeg', 70],
61 | ['jupyter', 'text/markdown', 80],
62 | ['jupyter', 'text/plain', 90],
63 | # LaTeX
64 | ['latex', 'text/latex', 10],
65 | ['latex', 'application/pdf', 20],
66 | ['latex', 'image/png', 30],
67 | ['latex', 'image/jpeg', 40],
68 | ['latex', 'text/markdown', 50],
69 | ['latex', 'text/plain', 60],
70 | ['latex', 'text/html', 70],
71 | # Link Checker
72 | ['linkcheck', 'text/plain', 10],
73 | ['linkcheck', 'text/html', 20],
74 | ]
75 | html_favicon: _static/lectures-favicon.ico
76 | html_theme: quantecon_book_theme
77 | html_static_path: ['_static']
78 | html_theme_options:
79 | authors:
80 | - name: Thomas J. Sargent
81 | url: http://www.tomsargent.com/
82 | - name: John Stachurski
83 | url: https://johnstachurski.net/
84 | header_organisation_url: https://quantecon.org
85 | header_organisation: QuantEcon
86 | repository_url: https://github.com/QuantEcon/lecture-python-advanced.myst
87 | nb_repository_url: https://github.com/QuantEcon/lecture-python-advanced.notebooks
88 | twitter: quantecon
89 | twitter_logo_url: https://assets.quantecon.org/img/qe-twitter-logo.png
90 | og_logo_url: https://assets.quantecon.org/img/qe-og-logo.png
91 | description: This website presents a set of lectures on advanced quantitative economic modeling, designed and written by Thomas J. Sargent and John Stachurski.
92 | keywords: Python, QuantEcon, Quantitative Economics, Economics, Sloan, Alfred P. Sloan Foundation, Tom J. Sargent, John Stachurski
93 | analytics:
94 | google_analytics_id: G-KZLV7PM9LL
95 | launch_buttons:
96 | colab_url : https://colab.research.google.com
97 | mathjax3_config:
98 | tex:
99 | macros:
100 | "argmax": "arg\\,max"
101 | "argmin": "arg\\,min"
102 | "col": "col"
103 | "Span": "span"
104 | "epsilon": "\\varepsilon"
105 | "EE": "\\mathbb{E}"
106 | "PP": "\\mathbb{P}"
107 | "RR": "\\mathbb{R}"
108 | "NN": "\\mathbb{N}"
109 | "ZZ": "\\mathbb{Z}"
110 | "aA": "\\mathcal{A}"
111 | "bB": "\\mathcal{B}"
112 | "cC": "\\mathcal{C}"
113 | "dD": "\\mathcal{D}"
114 | "eE": "\\mathcal{E}"
115 | "fF": "\\mathcal{F}"
116 | "gG": "\\mathcal{G}"
117 | "hH": "\\mathcal{H}"
118 | mathjax_path: https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js
119 | rediraffe_redirects:
120 | index_toc.md: intro.md
121 | tojupyter_static_file_path: ["source/_static", "_static"]
122 | tojupyter_target_html: true
123 | tojupyter_urlpath: "https://python-advanced.quantecon.org/"
124 | tojupyter_image_urlpath: "https://python-advanced.quantecon.org/_static/"
125 | tojupyter_lang_synonyms: ["ipython", "ipython3", "python"]
126 | tojupyter_kernels:
127 | python3:
128 | kernelspec:
129 | display_name: "Python"
130 | language: python3
131 | name: python3
132 | file_extension: ".py"
133 | tojupyter_images_markdown: true
134 |
--------------------------------------------------------------------------------
/.github/workflows/publish.yml:
--------------------------------------------------------------------------------
1 | name: Build & Publish to GH-PAGES
2 | on:
3 | push:
4 | tags:
5 | - 'publish*'
6 | jobs:
7 | publish:
8 | if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags')
9 | runs-on: ubuntu-latest
10 | steps:
11 | - name: Checkout
12 | uses: actions/checkout@v5
13 | - name: Setup Anaconda
14 | uses: conda-incubator/setup-miniconda@v3
15 | with:
16 | auto-update-conda: true
17 | auto-activate-base: true
18 | miniconda-version: 'latest'
19 | python-version: "3.13"
20 | environment-file: environment.yml
21 | activate-environment: quantecon
22 | - name: Install latex dependencies
23 | run: |
24 | sudo apt-get -qq update
25 | sudo apt-get install -y \
26 | texlive-latex-recommended \
27 | texlive-latex-extra \
28 | texlive-fonts-recommended \
29 | texlive-fonts-extra \
30 | texlive-xetex \
31 | latexmk \
32 | xindy \
33 | dvipng \
34 | cm-super
35 | - name: Display Conda Environment Versions
36 | shell: bash -l {0}
37 | run: conda list
38 | - name: Display Pip Versions
39 | shell: bash -l {0}
40 | run: pip list
41 | - name: Download "build" folder (cache)
42 | uses: dawidd6/action-download-artifact@v11
43 | with:
44 | workflow: cache.yml
45 | branch: main
46 | name: build-cache
47 | path: _build
48 | # Build Assets (Download Notebooks and PDF via LaTeX)
49 | - name: Build PDF from LaTeX
50 | shell: bash -l {0}
51 | run: |
52 | jb build lectures --builder pdflatex --path-output ./ -n --keep-going
53 | - name: Copy LaTeX PDF for GH-PAGES
54 | shell: bash -l {0}
55 | run: |
56 | mkdir -p _build/html/_pdf
57 | cp -u _build/latex/*.pdf _build/html/_pdf
58 | - name: Build Download Notebooks (sphinx-tojupyter)
59 | shell: bash -l {0}
60 | run: |
61 | jb build lectures --path-output ./ --builder=custom --custom-builder=jupyter
62 | - name: Copy Download Notebooks for GH-PAGES
63 | shell: bash -l {0}
64 | run: |
65 | mkdir -p _build/html/_notebooks
66 | cp -u _build/jupyter/*.ipynb _build/html/_notebooks
67 | # Build HTML (Website)
68 | # BUG: rm .doctress to remove `sphinx` rendering issues for ipywidget mimetypes
69 | # and clear the sphinx cache for building final HTML documents.
70 | - name: Build HTML
71 | shell: bash -l {0}
72 | run: |
73 | rm -r _build/.doctrees
74 | jb build lectures --path-output ./
75 | # Create HTML archive for release assets
76 | - name: Create HTML archive
77 | shell: bash -l {0}
78 | run: |
79 | tar -czf lecture-python-advanced-html-${{ github.ref_name }}.tar.gz -C _build/html .
80 | sha256sum lecture-python-advanced-html-${{ github.ref_name }}.tar.gz > html-checksum.txt
81 |
82 | # Create metadata manifest
83 | cat > html-manifest.json << EOF
84 | {
85 | "tag": "${{ github.ref_name }}",
86 | "commit": "${{ github.sha }}",
87 | "timestamp": "$(date -Iseconds)",
88 | "size_mb": $(du -sm _build/html | cut -f1),
89 | "file_count": $(find _build/html -type f | wc -l)
90 | }
91 | EOF
92 | - name: Upload archives to release
93 | uses: softprops/action-gh-release@v1
94 | with:
95 | files: |
96 | lecture-python-advanced-html-${{ github.ref_name }}.tar.gz
97 | html-checksum.txt
98 | html-manifest.json
99 | env:
100 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
101 | - name: Deploy website to gh-pages
102 | uses: peaceiris/actions-gh-pages@v4
103 | with:
104 | github_token: ${{ secrets.GITHUB_TOKEN }}
105 | publish_dir: _build/html/
106 | cname: python-advanced.quantecon.org
107 | - name: Upload "_build" folder (cache)
108 | uses: actions/upload-artifact@v4
109 | with:
110 | name: build-cache
111 | path: _build
112 | - name: Prepare lecture-python-advanced.notebooks sync
113 | shell: bash -l {0}
114 | run: |
115 | mkdir -p _build/lecture-python-advanced.notebooks
116 | cp -a _notebook_repo/. _build/lecture-python-advanced.notebooks
117 | cp _build/jupyter/*.ipynb _build/lecture-python-advanced.notebooks
118 | ls -a _build/lecture-python-advanced.notebooks
119 | - name: Commit latest notebooks to lecture-python-advanced.notebooks
120 | uses: cpina/github-action-push-to-another-repository@main
121 | env:
122 | API_TOKEN_GITHUB: ${{ secrets.QUANTECON_SERVICES_PAT }}
123 | with:
124 | source-directory: '_build/lecture-python-advanced.notebooks/'
125 | destination-repository-username: 'QuantEcon'
126 | destination-repository-name: 'lecture-python-advanced.notebooks'
127 | target-branch: 'main'
128 | commit-message: 'auto publishing updates to notebooks'
129 | destination-github-username: 'quantecon-services'
130 | user-email: services@quantecon.org
131 |
--------------------------------------------------------------------------------
/lectures/_static/lecture_specific/amss2/sequential_allocation.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | from scipy.optimize import root
3 | from quantecon import MarkovChain
4 |
5 |
6 | class SequentialAllocation:
7 |
8 | '''
9 | Class that takes CESutility or BGPutility object as input returns
10 | planner's allocation as a function of the multiplier on the
11 | implementability constraint μ.
12 | '''
13 |
14 | def __init__(self, model):
15 |
16 | # Initialize from model object attributes
17 | self.β, self.π, self.G = model.β, model.π, model.G
18 | self.mc, self.Θ = MarkovChain(self.π), model.Θ
19 | self.S = len(model.π) # Number of states
20 | self.model = model
21 |
22 | # Find the first best allocation
23 | self.find_first_best()
24 |
25 | def find_first_best(self):
26 | '''
27 | Find the first best allocation
28 | '''
29 | model = self.model
30 | S, Θ, G = self.S, self.Θ, self.G
31 | Uc, Un = model.Uc, model.Un
32 |
33 | def res(z):
34 | c = z[:S]
35 | n = z[S:]
36 | return np.hstack([Θ * Uc(c, n) + Un(c, n), Θ * n - c - G])
37 |
38 | res = root(res, np.full(2 * S, 0.5))
39 |
40 | if not res.success:
41 | raise Exception('Could not find first best')
42 |
43 | self.cFB = res.x[:S]
44 | self.nFB = res.x[S:]
45 |
46 | # Multiplier on the resource constraint
47 | self.ΞFB = Uc(self.cFB, self.nFB)
48 | self.zFB = np.hstack([self.cFB, self.nFB, self.ΞFB])
49 |
50 | def time1_allocation(self, μ):
51 | '''
52 | Computes optimal allocation for time t >= 1 for a given μ
53 | '''
54 | model = self.model
55 | S, Θ, G = self.S, self.Θ, self.G
56 | Uc, Ucc, Un, Unn = model.Uc, model.Ucc, model.Un, model.Unn
57 |
58 | def FOC(z):
59 | c = z[:S]
60 | n = z[S:2 * S]
61 | Ξ = z[2 * S:]
62 | # FOC of c
63 | return np.hstack([Uc(c, n) - μ * (Ucc(c, n) * c + Uc(c, n)) - Ξ,
64 | Un(c, n) - μ * (Unn(c, n) * n + Un(c, n)) \
65 | + Θ * Ξ, # FOC of n
66 | Θ * n - c - G])
67 |
68 | # Find the root of the first-order condition
69 | res = root(FOC, self.zFB)
70 | if not res.success:
71 | raise Exception('Could not find LS allocation.')
72 | z = res.x
73 | c, n, Ξ = z[:S], z[S:2 * S], z[2 * S:]
74 |
75 | # Compute x
76 | I = Uc(c, n) * c + Un(c, n) * n
77 | x = np.linalg.solve(np.eye(S) - self.β * self.π, I)
78 |
79 | return c, n, x, Ξ
80 |
81 | def time0_allocation(self, B_, s_0):
82 | '''
83 | Finds the optimal allocation given initial government debt B_ and
84 | state s_0
85 | '''
86 | model, π, Θ, G, β = self.model, self.π, self.Θ, self.G, self.β
87 | Uc, Ucc, Un, Unn = model.Uc, model.Ucc, model.Un, model.Unn
88 |
89 | # First order conditions of planner's problem
90 | def FOC(z):
91 | μ, c, n, Ξ = z
92 | xprime = self.time1_allocation(μ)[2]
93 | return np.hstack([Uc(c, n) * (c - B_) + Un(c, n) * n + β * π[s_0]
94 | @ xprime,
95 | Uc(c, n) - μ * (Ucc(c, n)
96 | * (c - B_) + Uc(c, n)) - Ξ,
97 | Un(c, n) - μ * (Unn(c, n) * n
98 | + Un(c, n)) + Θ[s_0] * Ξ,
99 | (Θ * n - c - G)[s_0]])
100 |
101 | # Find root
102 | res = root(FOC, np.array(
103 | [0, self.cFB[s_0], self.nFB[s_0], self.ΞFB[s_0]]))
104 | if not res.success:
105 | raise Exception('Could not find time 0 LS allocation.')
106 |
107 | return res.x
108 |
109 | def time1_value(self, μ):
110 | '''
111 | Find the value associated with multiplier μ
112 | '''
113 | c, n, x, Ξ = self.time1_allocation(μ)
114 | U = self.model.U(c, n)
115 | V = np.linalg.solve(np.eye(self.S) - self.β * self.π, U)
116 | return c, n, x, V
117 |
118 | def Τ(self, c, n):
119 | '''
120 | Computes Τ given c, n
121 | '''
122 | model = self.model
123 | Uc, Un = model.Uc(c, n), model.Un(c, n)
124 |
125 | return 1 + Un / (self.Θ * Uc)
126 |
127 | def simulate(self, B_, s_0, T, sHist=None):
128 | '''
129 | Simulates planners policies for T periods
130 | '''
131 | model, π, β = self.model, self.π, self.β
132 | Uc = model.Uc
133 |
134 | if sHist is None:
135 | sHist = self.mc.simulate(T, s_0)
136 |
137 | cHist, nHist, Bhist, ΤHist, μHist = np.zeros((5, T))
138 | RHist = np.zeros(T - 1)
139 |
140 | # Time 0
141 | μ, cHist[0], nHist[0], _ = self.time0_allocation(B_, s_0)
142 | ΤHist[0] = self.Τ(cHist[0], nHist[0])[s_0]
143 | Bhist[0] = B_
144 | μHist[0] = μ
145 |
146 | # Time 1 onward
147 | for t in range(1, T):
148 | c, n, x, Ξ = self.time1_allocation(μ)
149 | Τ = self.Τ(c, n)
150 | u_c = Uc(c, n)
151 | s = sHist[t]
152 | Eu_c = π[sHist[t - 1]] @ u_c
153 | cHist[t], nHist[t], Bhist[t], ΤHist[t] = c[s], n[s], x[s] / u_c[s], \
154 | Τ[s]
155 | RHist[t - 1] = Uc(cHist[t - 1], nHist[t - 1]) / (β * Eu_c)
156 | μHist[t] = μ
157 |
158 | return [cHist, nHist, Bhist, ΤHist, sHist, μHist, RHist]
159 |
--------------------------------------------------------------------------------
/lectures/_static/lecture_specific/opt_tax_recur/sequential_allocation.py:
--------------------------------------------------------------------------------
1 | class SequentialLS:
2 |
3 | '''
4 | Class that takes a preference object, state transition matrix,
5 | and state contingent government expenditure plan as inputs, and
6 | solves the sequential allocation problem described above.
7 | It returns optimal allocations about consumption and labor supply,
8 | as well as the multiplier on the implementability constraint Φ.
9 | '''
10 |
11 | def __init__(self,
12 | pref,
13 | π=np.full((2, 2), 0.5),
14 | g=np.array([0.1, 0.2])):
15 |
16 | # Initialize from pref object attributes
17 | self.β, self.π, self.g = pref.β, π, g
18 | self.mc = MarkovChain(self.π)
19 | self.S = len(π) # Number of states
20 | self.pref = pref
21 |
22 | # Find the first best allocation
23 | self.find_first_best()
24 |
25 | def FOC_first_best(self, c, g):
26 | '''
27 | First order conditions that characterize
28 | the first best allocation.
29 | '''
30 |
31 | pref = self.pref
32 | Uc, Ul = pref.Uc, pref.Ul
33 |
34 | n = c + g
35 | l = 1 - n
36 |
37 | return Uc(c, l) - Ul(c, l)
38 |
39 | def find_first_best(self):
40 | '''
41 | Find the first best allocation
42 | '''
43 | S, g = self.S, self.g
44 |
45 | res = root(self.FOC_first_best, np.full(S, 0.5), args=(g,))
46 |
47 | if (res.fun > 1e-10).any():
48 | raise Exception('Could not find first best')
49 |
50 | self.cFB = res.x
51 | self.nFB = self.cFB + g
52 |
53 | def FOC_time1(self, c, Φ, g):
54 | '''
55 | First order conditions that characterize
56 | optimal time 1 allocation problems.
57 | '''
58 |
59 | pref = self.pref
60 | Uc, Ucc, Ul, Ull, Ulc = pref.Uc, pref.Ucc, pref.Ul, pref.Ull, pref.Ulc
61 |
62 | n = c + g
63 | l = 1 - n
64 |
65 | LHS = (1 + Φ) * Uc(c, l) + Φ * (c * Ucc(c, l) - n * Ulc(c, l))
66 | RHS = (1 + Φ) * Ul(c, l) + Φ * (c * Ulc(c, l) - n * Ull(c, l))
67 |
68 | diff = LHS - RHS
69 |
70 | return diff
71 |
72 | def time1_allocation(self, Φ):
73 | '''
74 | Computes optimal allocation for time t >= 1 for a given Φ
75 | '''
76 | pref = self.pref
77 | S, g = self.S, self.g
78 |
79 | # use the first best allocation as intial guess
80 | res = root(self.FOC_time1, self.cFB, args=(Φ, g))
81 |
82 | if (res.fun > 1e-10).any():
83 | raise Exception('Could not find LS allocation.')
84 |
85 | c = res.x
86 | n = c + g
87 | l = 1 - n
88 |
89 | # Compute x
90 | I = pref.Uc(c, n) * c - pref.Ul(c, l) * n
91 | x = np.linalg.solve(np.eye(S) - self.β * self.π, I)
92 |
93 | return c, n, x
94 |
95 | def FOC_time0(self, c0, Φ, g0, b0):
96 | '''
97 | First order conditions that characterize
98 | time 0 allocation problem.
99 | '''
100 |
101 | pref = self.pref
102 | Ucc, Ulc = pref.Ucc, pref.Ulc
103 |
104 | n0 = c0 + g0
105 | l0 = 1 - n0
106 |
107 | diff = self.FOC_time1(c0, Φ, g0)
108 | diff -= Φ * (Ucc(c0, l0) - Ulc(c0, l0)) * b0
109 |
110 | return diff
111 |
112 | def implementability(self, Φ, b0, s0, cn0_arr):
113 | '''
114 | Compute the differences between the RHS and LHS
115 | of the implementability constraint given Φ,
116 | initial debt, and initial state.
117 | '''
118 |
119 | pref, π, g, β = self.pref, self.π, self.g, self.β
120 | Uc, Ul = pref.Uc, pref.Ul
121 | g0 = self.g[s0]
122 |
123 | c, n, x = self.time1_allocation(Φ)
124 |
125 | res = root(self.FOC_time0, cn0_arr[0], args=(Φ, g0, b0))
126 | c0 = res.x
127 | n0 = c0 + g0
128 | l0 = 1 - n0
129 |
130 | cn0_arr[:] = c0.item(), n0.item()
131 |
132 | LHS = Uc(c0, l0) * b0
133 | RHS = Uc(c0, l0) * c0 - Ul(c0, l0) * n0 + β * π[s0] @ x
134 |
135 | return RHS - LHS
136 |
137 | def time0_allocation(self, b0, s0):
138 | '''
139 | Finds the optimal time 0 allocation given
140 | initial government debt b0 and state s0
141 | '''
142 |
143 | # use the first best allocation as initial guess
144 | cn0_arr = np.array([self.cFB[s0], self.nFB[s0]])
145 |
146 | res = root(self.implementability, 0., args=(b0, s0, cn0_arr))
147 |
148 | if (res.fun > 1e-10).any():
149 | raise Exception('Could not find time 0 LS allocation.')
150 |
151 | Φ = res.x[0]
152 | c0, n0 = cn0_arr
153 |
154 | return Φ, c0, n0
155 |
156 | def τ(self, c, n):
157 | '''
158 | Computes τ given c, n
159 | '''
160 | pref = self.pref
161 | Uc, Ul = pref.Uc, pref.Ul
162 |
163 | return 1 - Ul(c, 1-n) / Uc(c, 1-n)
164 |
165 | def simulate(self, b0, s0, T, sHist=None):
166 | '''
167 | Simulates planners policies for T periods
168 | '''
169 | pref, π, β = self.pref, self.π, self.β
170 | Uc = pref.Uc
171 |
172 | if sHist is None:
173 | sHist = self.mc.simulate(T, s0)
174 |
175 | cHist, nHist, Bhist, τHist, ΦHist = np.empty((5, T))
176 | RHist = np.empty(T-1)
177 |
178 | # Time 0
179 | Φ, cHist[0], nHist[0] = self.time0_allocation(b0, s0)
180 | τHist[0] = self.τ(cHist[0], nHist[0])
181 | Bhist[0] = b0
182 | ΦHist[0] = Φ
183 |
184 | # Time 1 onward
185 | for t in range(1, T):
186 | c, n, x = self.time1_allocation(Φ)
187 | τ = self.τ(c, n)
188 | u_c = Uc(c, 1-n)
189 | s = sHist[t]
190 | Eu_c = π[sHist[t-1]] @ u_c
191 | cHist[t], nHist[t], Bhist[t], τHist[t] = c[s], n[s], x[s] / u_c[s], τ[s]
192 | RHist[t-1] = Uc(cHist[t-1], 1-nHist[t-1]) / (β * Eu_c)
193 | ΦHist[t] = Φ
194 |
195 | gHist = self.g[sHist]
196 | yHist = nHist
197 |
198 | return [cHist, nHist, Bhist, τHist, gHist, yHist, sHist, ΦHist, RHist]
199 |
--------------------------------------------------------------------------------
/lectures/_static/lecture_specific/opt_tax_recur/recursive_allocation.py:
--------------------------------------------------------------------------------
1 | class RecursiveLS:
2 |
3 | '''
4 | Compute the planner's allocation by solving Bellman
5 | equation.
6 | '''
7 |
8 | def __init__(self,
9 | pref,
10 | x_grid,
11 | π=np.full((2, 2), 0.5),
12 | g=np.array([0.1, 0.2])):
13 |
14 | self.π, self.g, self.S = π, g, len(π)
15 | self.pref, self.x_grid = pref, x_grid
16 |
17 | bounds = np.empty((self.S, 2))
18 |
19 | # bound for n
20 | bounds[0] = 0, 1
21 |
22 | # bound for xprime
23 | for s in range(self.S-1):
24 | bounds[s+1] = x_grid.min(), x_grid.max()
25 |
26 | self.bounds = bounds
27 |
28 | # initialization of time 1 value function
29 | self.V = None
30 |
31 | def time1_allocation(self, V=None, tol=1e-7):
32 | '''
33 | Solve the optimal time 1 allocation problem
34 | by iterating Bellman value function.
35 | '''
36 |
37 | π, g, S = self.π, self.g, self.S
38 | pref, x_grid, bounds = self.pref, self.x_grid, self.bounds
39 |
40 | # initial guess of value function
41 | if V is None:
42 | V = np.zeros((len(x_grid), S))
43 |
44 | # initial guess of policy
45 | z = np.empty((len(x_grid), S, S+2))
46 |
47 | # guess of n
48 | z[:, :, 1] = 0.5
49 |
50 | # guess of xprime
51 | for s in range(S):
52 | for i in range(S-1):
53 | z[:, s, i+2] = x_grid
54 |
55 | while True:
56 | # value function iteration
57 | V_new, z_new = T(V, z, pref, π, g, x_grid, bounds)
58 |
59 | if np.max(np.abs(V - V_new)) < tol:
60 | break
61 |
62 | V = V_new
63 | z = z_new
64 |
65 | self.V = V_new
66 | self.z1 = z_new
67 | self.c1 = z_new[:, :, 0]
68 | self.n1 = z_new[:, :, 1]
69 | self.xprime1 = z_new[:, :, 2:]
70 |
71 | return V_new, z_new
72 |
73 | def time0_allocation(self, b0, s0):
74 | '''
75 | Find the optimal time 0 allocation by maximization.
76 | '''
77 |
78 | if self.V is None:
79 | self.time1_allocation()
80 |
81 | π, g, S = self.π, self.g, self.S
82 | pref, x_grid, bounds = self.pref, self.x_grid, self.bounds
83 | V, z1 = self.V, self.z1
84 |
85 | x = 1. # x is arbitrary
86 | res = nelder_mead(obj_V,
87 | z1[0, s0, 1:-1],
88 | args=(x, s0, V, pref, π, g, x_grid, b0),
89 | bounds=bounds,
90 | tol_f=1e-10)
91 |
92 | n0, xprime0 = IC(res.x, x, s0, b0, pref, π, g)
93 | c0 = n0 - g[s0]
94 | z0 = np.array([c0, n0, *xprime0])
95 |
96 | self.z0 = z0
97 | self.n0 = n0
98 | self.c0 = n0 - g[s0]
99 | self.xprime0 = xprime0
100 |
101 | return z0
102 |
103 | def τ(self, c, n):
104 | '''
105 | Computes τ given c, n
106 | '''
107 | pref = self.pref
108 | uc, ul = pref.Uc(c, 1-n), pref.Ul(c, 1-n)
109 |
110 | return 1 - ul / uc
111 |
112 | def simulate(self, b0, s0, T, sHist=None):
113 | '''
114 | Simulates Ramsey plan for T periods
115 | '''
116 | pref, π = self.pref, self.π
117 | Uc = pref.Uc
118 |
119 | if sHist is None:
120 | sHist = self.mc.simulate(T, s0)
121 |
122 | cHist, nHist, Bhist, τHist, xHist = np.empty((5, T))
123 | RHist = np.zeros(T-1)
124 |
125 | # Time 0
126 | self.time0_allocation(b0, s0)
127 | cHist[0], nHist[0], xHist[0] = self.c0, self.n0, self.xprime0[s0]
128 | τHist[0] = self.τ(cHist[0], nHist[0])
129 | Bhist[0] = b0
130 |
131 | # Time 1 onward
132 | for t in range(1, T):
133 | s, x = sHist[t], xHist[t-1]
134 | cHist[t] = np.interp(x, self.x_grid, self.c1[:, s])
135 | nHist[t] = np.interp(x, self.x_grid, self.n1[:, s])
136 |
137 | τHist[t] = self.τ(cHist[t], nHist[t])
138 |
139 | Bhist[t] = x / Uc(cHist[t], 1-nHist[t])
140 |
141 | c, n = np.empty((2, self.S))
142 | for sprime in range(self.S):
143 | c[sprime] = np.interp(x, x_grid, self.c1[:, sprime])
144 | n[sprime] = np.interp(x, x_grid, self.n1[:, sprime])
145 | Euc = π[sHist[t-1]] @ Uc(c, 1-n)
146 | RHist[t-1] = Uc(cHist[t-1], 1-nHist[t-1]) / (self.pref.β * Euc)
147 |
148 | gHist = self.g[sHist]
149 | yHist = nHist
150 |
151 | if t < T-1:
152 | sprime = sHist[t+1]
153 | xHist[t] = np.interp(x, self.x_grid, self.xprime1[:, s, sprime])
154 |
155 | return [cHist, nHist, Bhist, τHist, gHist, yHist, xHist, RHist]
156 |
157 | # Helper functions
158 |
159 | @njit(parallel=True)
160 | def T(V, z, pref, π, g, x_grid, bounds):
161 | '''
162 | One step iteration of Bellman value function.
163 | '''
164 |
165 | S = len(π)
166 |
167 | V_new = np.empty_like(V)
168 | z_new = np.empty_like(z)
169 |
170 | for i in prange(len(x_grid)):
171 | x = x_grid[i]
172 | for s in prange(S):
173 | res = nelder_mead(obj_V,
174 | z[i, s, 1:-1],
175 | args=(x, s, V, pref, π, g, x_grid),
176 | bounds=bounds,
177 | tol_f=1e-10)
178 |
179 | # optimal policy
180 | n, xprime = IC(res.x, x, s, None, pref, π, g)
181 | z_new[i, s, 0] = n - g[s] # c
182 | z_new[i, s, 1] = n # n
183 | z_new[i, s, 2:] = xprime # xprime
184 |
185 | V_new[i, s] = res.fun
186 |
187 | return V_new, z_new
188 |
189 | @njit
190 | def obj_V(z_sub, x, s, V, pref, π, g, x_grid, b0=None):
191 | '''
192 | The objective on the right hand side of the Bellman equation.
193 | z_sub contains guesses of n and xprime[:-1].
194 | '''
195 |
196 | S = len(π)
197 | β, U = pref.β, pref.U
198 |
199 | # find (n, xprime) that satisfies implementability constraint
200 | n, xprime = IC(z_sub, x, s, b0, pref, π, g)
201 | c, l = n-g[s], 1-n
202 |
203 | # if xprime[-1] violates bound, return large penalty
204 | if (xprime[-1] < x_grid.min()):
205 | return -1e9 * (1 + np.abs(xprime[-1] - x_grid.min()))
206 | elif (xprime[-1] > x_grid.max()):
207 | return -1e9 * (1 + np.abs(xprime[-1] - x_grid.max()))
208 |
209 | # prepare Vprime vector
210 | Vprime = np.empty(S)
211 | for sprime in range(S):
212 | Vprime[sprime] = np.interp(xprime[sprime], x_grid, V[:, sprime])
213 |
214 | # compute the objective value
215 | obj = U(c, l) + β * π[s] @ Vprime
216 |
217 | return obj
218 |
219 | @njit
220 | def IC(z_sub, x, s, b0, pref, π, g):
221 | '''
222 | Find xprime[-1] that satisfies the implementability condition
223 | given the guesses of n and xprime[:-1].
224 | '''
225 |
226 | β, Uc, Ul = pref.β, pref.Uc, pref.Ul
227 |
228 | n = z_sub[0]
229 | xprime = np.empty(len(π))
230 | xprime[:-1] = z_sub[1:]
231 |
232 | c, l = n-g[s], 1-n
233 | uc = Uc(c, l)
234 | ul = Ul(c, l)
235 |
236 | if b0 is None:
237 | diff = x
238 | else:
239 | diff = uc * b0
240 |
241 | diff -= uc * (n - g[s]) - ul * n + β * π[s][:-1] @ xprime[:-1]
242 | xprime[-1] = diff / (β * π[s][-1])
243 |
244 | return n, xprime
245 |
--------------------------------------------------------------------------------
/lectures/lucas_asset_pricing_dles.md:
--------------------------------------------------------------------------------
1 | ---
2 | jupytext:
3 | text_representation:
4 | extension: .md
5 | format_name: myst
6 | kernelspec:
7 | display_name: Python 3
8 | language: python
9 | name: python3
10 | ---
11 |
12 | (lucas_asset_pricing_dles)=
13 | ```{raw} html
14 |
19 | ```
20 |
21 | ```{index} single: python
22 | ```
23 |
24 | # Lucas Asset Pricing Using DLE
25 |
26 | This is one of a suite of lectures that use the quantecon DLE class to instantiate models within the
27 | {cite}`HS2013` class of models described in detail in {doc}`Recursive Models of Dynamic Linear Economies `.
28 |
29 | In addition to what's in Anaconda, this lecture uses the quantecon library
30 |
31 | ```{code-cell} ipython
32 | ---
33 | tags: [hide-output]
34 | ---
35 | !pip install --upgrade quantecon
36 | ```
37 |
38 | This lecture uses the DLE class to price payout
39 | streams that are linear functions of the economy's state vector, as well
40 | as risk-free assets that pay out one unit of the first consumption good
41 | with certainty.
42 |
43 | We assume basic knowledge of the class of economic environments that fall within the domain of the
44 | DLE class.
45 |
46 | Many details about the basic environment are contained in the lecture
47 | {doc}`Growth in Dynamic Linear Economies `.
48 |
49 | We'll also need the following imports
50 |
51 | ```{code-cell} ipython
52 | import numpy as np
53 | import matplotlib.pyplot as plt
54 | from quantecon import DLE
55 | ```
56 |
57 | We use a linear-quadratic version of an economy that Lucas (1978) {cite}`Lucas1978` used
58 | to develop an equilibrium theory of asset prices:
59 |
60 | **Preferences**
61 |
62 | $$
63 | -\frac{1}{2}\mathbb{E}\sum_{t=0}^\infty \beta^t[(c_t - b_t)^2 + l_t^2]|J_0
64 | $$
65 |
66 | $$
67 | s_t = c_t
68 | $$
69 |
70 | $$
71 | b_t = U_bz_t
72 | $$
73 |
74 | **Technology**
75 |
76 | $$
77 | c_t = d_{1t}
78 | $$
79 |
80 | $$
81 | k_t = \delta_k k_{t-1} + i_t
82 | $$
83 |
84 | $$
85 | g_t = \phi_1 i_t \, , \phi_1 > 0
86 | $$
87 |
88 | $$
89 | \left[ {\begin{array}{c}
90 | d_{1t} \\ 0
91 | \end{array} }
92 | \right] = U_dz_t
93 | $$
94 |
95 | **Information**
96 |
97 | $$
98 | z_{t+1} =
99 | \left[ {\begin{array}{ccc}
100 | 1 & 0 & 0 \\ 0 & 0.8 & 0 \\ 0 & 0 & 0.5
101 | \end{array} }
102 | \right]
103 | z_t +
104 | \left[ {\begin{array}{cc}
105 | 0 & 0 \\ 1 & 0 \\ 0 & 1
106 | \end{array} }
107 | \right]
108 | w_{t+1}
109 | $$
110 |
111 | $$
112 | U_b =
113 | \left[ {\begin{array}{ccc}
114 | 30 & 0 & 0
115 | \end{array} }
116 | \right]
117 | $$
118 |
119 | $$
120 | U_d =
121 | \left[ {\begin{array}{ccc}
122 | 5 & 1 & 0 \\ 0 & 0 & 0
123 | \end{array} }
124 | \right]
125 | $$
126 |
127 | $$
128 | x_0 =
129 | \left[ {\begin{array}{ccccc}
130 | 5 & 150 & 1 & 0 & 0
131 | \end{array} }
132 | \right]'
133 | $$
134 |
135 | ## Asset pricing equations
136 |
137 | {cite}`HS2013` show that the time t value of a permanent claim to a stream
138 | $y_s = U_ax_s \, , s \geq t$ is:
139 |
140 | $$
141 | a_t = (x_t'\mu_ax_t + \sigma_a)/(\bar e _1M_cx_t)
142 | $$
143 |
144 | with
145 |
146 | $$
147 | \mu_a = \sum_{\tau = 0}^\infty \beta^\tau(A^{o'})^\tau Z_a A^{o\tau}
148 | $$
149 |
150 | $$
151 | \sigma_a = \frac{\beta}{1-\beta} \text{trace} (Z_a \sum_{\tau = 0}^\infty \beta^\tau (A^{o})^\tau C C^{'} (A^{o'})^\tau)
152 | $$
153 |
154 | where
155 |
156 | $$
157 | Z_a = U_a^{'}M_c
158 | $$
159 |
160 | The use of $\bar e _1$ indicates that the first consumption good
161 | is the numeraire.
162 |
163 | ## Asset pricing simulations
164 |
165 | ```{code-cell} python3
166 | gam = 0
167 | γ = np.array([[gam], [0]])
168 | ϕ_c = np.array([[1], [0]])
169 | ϕ_g = np.array([[0], [1]])
170 | ϕ_1 = 1e-4
171 | ϕ_i = np.array([[0], [-ϕ_1]])
172 | δ_k = np.array([[.95]])
173 | θ_k = np.array([[1]])
174 | β = np.array([[1 / 1.05]])
175 | ud = np.array([[5, 1, 0],
176 | [0, 0, 0]])
177 | a22 = np.array([[1, 0, 0],
178 | [0, 0.8, 0],
179 | [0, 0, 0.5]])
180 | c2 = np.array([[0, 1, 0],
181 | [0, 0, 1]]).T
182 | l_λ = np.array([[0]])
183 | π_h = np.array([[1]])
184 | δ_h = np.array([[.9]])
185 | θ_h = np.array([[1]]) - δ_h
186 | ub = np.array([[30, 0, 0]])
187 | x0 = np.array([[5, 150, 1, 0, 0]]).T
188 |
189 | info1 = (a22, c2, ub, ud)
190 | tech1 = (ϕ_c, ϕ_g, ϕ_i, γ, δ_k, θ_k)
191 | pref1 = (β, l_λ, π_h, δ_h, θ_h)
192 | ```
193 |
194 | ```{code-cell} python3
195 | econ1 = DLE(info1, tech1, pref1)
196 | ```
197 |
198 | After specifying a "Pay" matrix, we simulate the economy.
199 |
200 | The particular choice of "Pay" used below means that we are pricing a
201 | perpetual claim on the endowment process $d_{1t}$
202 |
203 | ```{code-cell} python3
204 | econ1.compute_sequence(x0, ts_length=100, Pay=np.array([econ1.Sd[0, :]]))
205 | ```
206 |
207 | The graph below plots the price of this claim over time:
208 |
209 | ```{code-cell} python3
210 | ### Fig 7.12.1 from p.147 of HS2013
211 | plt.plot(econ1.Pay_Price, label='Price of Tree')
212 | plt.legend()
213 | plt.show()
214 | ```
215 |
216 | The next plot displays the realized gross rate of return on this "Lucas
217 | tree" as well as on a risk-free one-period bond:
218 |
219 | ```{code-cell} python3
220 | ### Left panel of Fig 7.12.2 from p.148 of HS2013
221 | plt.plot(econ1.Pay_Gross, label='Tree')
222 | plt.plot(econ1.R1_Gross, label='Risk-Free')
223 | plt.legend()
224 | plt.show()
225 | ```
226 |
227 | ```{code-cell} python3
228 | np.corrcoef(econ1.Pay_Gross[1:, 0], econ1.R1_Gross[1:, 0])
229 | ```
230 |
231 | Above we have also calculated the correlation coefficient between these
232 | two returns.
233 |
234 | To give an idea of how the term structure of interest rates moves in
235 | this economy, the next plot displays the *net* rates of return on
236 | one-period and five-period risk-free bonds:
237 |
238 | ```{code-cell} python3
239 | ### Right panel of Fig 7.12.2 from p.148 of HS2013
240 | plt.plot(econ1.R1_Net, label='One-Period')
241 | plt.plot(econ1.R5_Net, label='Five-Period')
242 | plt.legend()
243 | plt.show()
244 | ```
245 |
246 | From the above plot, we can see the tendency of the term structure to
247 | slope up when rates are low and to slope down when rates are high.
248 |
249 | Comparing it to the previous plot of the price of the "Lucas tree", we
250 | can also see that net rates of return are low when the price of the tree
251 | is high, and vice versa.
252 |
253 | We now plot the realized gross rate of return on a "Lucas tree" as well
254 | as on a risk-free one-period bond when the autoregressive parameter for
255 | the endowment process is reduced to 0.4:
256 |
257 | ```{code-cell} python3
258 | a22_2 = np.array([[1, 0, 0],
259 | [0, 0.4, 0],
260 | [0, 0, 0.5]])
261 | info2 = (a22_2, c2, ub, ud)
262 |
263 | econ2 = DLE(info2, tech1, pref1)
264 | econ2.compute_sequence(x0, ts_length=100, Pay=np.array([econ2.Sd[0, :]]))
265 | ```
266 |
267 | ```{code-cell} python3
268 | ### Left panel of Fig 7.12.3 from p.148 of HS2013
269 | plt.plot(econ2.Pay_Gross, label='Tree')
270 | plt.plot(econ2.R1_Gross, label='Risk-Free')
271 | plt.legend()
272 | plt.show()
273 | ```
274 |
275 | ```{code-cell} python3
276 | np.corrcoef(econ2.Pay_Gross[1:, 0], econ2.R1_Gross[1:, 0])
277 | ```
278 |
279 | The correlation between these two gross rates is now more negative.
280 |
281 | Next, we again plot the *net* rates of return on one-period and
282 | five-period risk-free bonds:
283 |
284 | ```{code-cell} python3
285 | ### Right panel of Fig 7.12.3 from p.148 of HS2013
286 | plt.plot(econ2.R1_Net, label='One-Period')
287 | plt.plot(econ2.R5_Net, label='Five-Period')
288 | plt.legend()
289 | plt.show()
290 | ```
291 |
292 | We can see the tendency of the term structure to slope up when rates are
293 | low (and down when rates are high) has been accentuated relative to the
294 | first instance of our economy.
295 |
--------------------------------------------------------------------------------
/lectures/irfs_in_hall_model.md:
--------------------------------------------------------------------------------
1 | ---
2 | jupytext:
3 | text_representation:
4 | extension: .md
5 | format_name: myst
6 | kernelspec:
7 | display_name: Python 3
8 | language: python
9 | name: python3
10 | ---
11 |
12 | (irfs_in_hall_model)=
13 | ```{raw} html
14 |
19 | ```
20 |
21 | ```{index} single: python
22 | ```
23 |
24 | # IRFs in Hall Models
25 |
26 | This is another member of a suite of lectures that use the quantecon DLE class to instantiate models within the
27 | {cite}`HS2013` class of models described in detail in {doc}`Recursive Models of Dynamic Linear Economies `.
28 |
29 | In addition to what's in Anaconda, this lecture uses the quantecon library.
30 |
31 | ```{code-cell} ipython
32 | ---
33 | tags: [hide-output]
34 | ---
35 | !pip install --upgrade quantecon
36 | ```
37 |
38 | We'll make these imports:
39 |
40 | ```{code-cell} ipython
41 | import numpy as np
42 | import matplotlib.pyplot as plt
43 | from quantecon import DLE
44 | ```
45 |
46 | This lecture shows how the DLE class can be used to create impulse
47 | response functions for three related economies, starting from
48 | Hall (1978) {cite}`Hall1978`.
49 |
50 | Knowledge of the basic economic environment is assumed.
51 |
52 | See the lecture "Growth in Dynamic Linear Economies" for more details.
53 |
54 | ## Example 1: Hall (1978)
55 |
56 | First, we set parameters to make consumption (almost) follow a random
57 | walk.
58 |
59 | We set
60 |
61 | $$
62 | \lambda = 0, \pi = 1, \gamma_1 = 0.1, \phi_1 = 0.00001, \delta_k = 0.95, \beta = \frac{1}{1.05}
63 | $$
64 |
65 | (In this example $\delta_h$ and $\theta_h$ are arbitrary as
66 | household capital does not enter the equation for consumption services.
67 |
68 | We set them to values that will become useful in Example 3)
69 |
70 | It is worth noting that this choice of parameter values ensures that
71 | $\beta(\gamma_1 + \delta_k) = 1$.
72 |
73 | For simulations of this economy, we choose an initial condition of:
74 |
75 | $$
76 | x_0 =
77 | \left[ {\begin{array}{ccccc}
78 | 5 & 150 & 1 & 0 & 0
79 | \end{array} }
80 | \right]'
81 | $$
82 |
83 | ```{code-cell} python3
84 | γ_1 = 0.1
85 | γ = np.array([[γ_1], [0]])
86 | ϕ_c = np.array([[1], [0]])
87 | ϕ_g = np.array([[0], [1]])
88 | ϕ_1 = 1e-5
89 | ϕ_i = np.array([[1], [-ϕ_1]])
90 | δ_k = np.array([[.95]])
91 | θ_k = np.array([[1]])
92 | β = np.array([[1 / 1.05]])
93 | l_λ = np.array([[0]])
94 | π_h = np.array([[1]])
95 | δ_h = np.array([[.9]])
96 | θ_h = np.array([[1]])
97 | a22 = np.array([[1, 0, 0],
98 | [0, 0.8, 0],
99 | [0, 0, 0.5]])
100 | c2 = np.array([[0, 0],
101 | [1, 0],
102 | [0, 1]])
103 | ud = np.array([[5, 1, 0],
104 | [0, 0, 0]])
105 | ub = np.array([[30, 0, 0]])
106 | x0 = np.array([[5], [150], [1], [0], [0]])
107 |
108 | info1 = (a22, c2, ub, ud)
109 | tech1 = (ϕ_c, ϕ_g, ϕ_i, γ, δ_k, θ_k)
110 | pref1 = (β, l_λ, π_h, δ_h, θ_h)
111 | ```
112 |
113 | These parameter values are used to define an economy of the DLE class.
114 |
115 | We can then simulate the economy for a chosen length of time, from our
116 | initial state vector $x_0$.
117 |
118 | The economy stores the simulated values for each variable. Below we plot
119 | consumption and investment:
120 |
121 | ```{code-cell} python3
122 | econ1 = DLE(info1, tech1, pref1)
123 | econ1.compute_sequence(x0, ts_length=300)
124 |
125 | # This is the right panel of Fig 5.7.1 from p.105 of HS2013
126 | plt.plot(econ1.c[0], label='Cons.')
127 | plt.plot(econ1.i[0], label='Inv.')
128 | plt.legend()
129 | plt.show()
130 | ```
131 |
132 | The DLE class can be used to create impulse response functions for each
133 | of the endogenous variables: $\{c_t,s_t,h_t,i_t,k_t,g_t\}$.
134 |
135 | If no selector vector for the shock is specified, the default choice is
136 | to give IRFs to the first shock in $w_{t+1}$.
137 |
138 | Below we plot the impulse response functions of investment and
139 | consumption to an endowment innovation (the first shock) in the Hall
140 | model:
141 |
142 | ```{code-cell} python3
143 | econ1.irf(ts_length=40, shock=None)
144 | # This is the left panel of Fig 5.7.1 from p.105 of HS2013
145 | plt.plot(econ1.c_irf, label='Cons.')
146 | plt.plot(econ1.i_irf, label='Inv.')
147 | plt.legend()
148 | plt.show()
149 | ```
150 |
151 | It can be seen that the endowment shock has permanent effects on the
152 | level of both consumption and investment, consistent with the endogenous
153 | unit eigenvalue in this economy.
154 |
155 | Investment is much more responsive to the endowment shock at shorter time
156 | horizons.
157 |
158 | ## Example 2: higher adjustment costs
159 |
160 | We generate our next economy by making only one change to the parameters
161 | of Example 1: we raise the parameter associated with the cost of
162 | adjusting capital,$\phi_1$, from 0.00001 to 0.2.
163 |
164 | This will lower the endogenous eigenvalue that is unity in Example 1 to
165 | a value slightly below 1.
166 |
167 | ```{code-cell} python3
168 | ϕ_12 = 0.2
169 | ϕ_i2 = np.array([[1], [-ϕ_12]])
170 | tech2 = (ϕ_c, ϕ_g, ϕ_i2, γ, δ_k, θ_k)
171 |
172 | econ2 = DLE(info1, tech2, pref1)
173 | econ2.compute_sequence(x0, ts_length = 300)
174 |
175 | # This is the right panel of Fig 5.8.1 from p.106 of HS2013
176 | plt.plot(econ2.c[0], label='Cons.')
177 | plt.plot(econ2.i[0], label='Inv.')
178 | plt.legend()
179 | plt.show()
180 | ```
181 |
182 | ```{code-cell} python3
183 | econ2.irf(ts_length=40,shock=None)
184 | # This is the left panel of Fig 5.8.1 from p.106 of HS2013
185 | plt.plot(econ2.c_irf,label='Cons.')
186 | plt.plot(econ2.i_irf,label='Inv.')
187 | plt.legend()
188 | plt.show()
189 | ```
190 |
191 | ```{code-cell} python3
192 | econ2.endo
193 | ```
194 |
195 | ```{code-cell} python3
196 | econ2.compute_steadystate()
197 | print(econ2.css, econ2.iss, econ2.kss)
198 | ```
199 |
200 | The first graph shows that there seems to be a downward trend in both
201 | consumption and investment.
202 |
203 | This is a consequence of the decrease in the largest endogenous
204 | eigenvalue from unity in the earlier economy, caused by the higher
205 | adjustment cost.
206 |
207 | The present economy has a nonstochastic steady state value of 5 for
208 | consumption and 0 for both capital and investment.
209 |
210 | Because the largest endogenous eigenvalue is still close to 1, the
211 | economy heads only slowly towards these mean values.
212 |
213 | The impulse response functions now show that an endowment shock does not
214 | have a permanent effect on the levels of either consumption or
215 | investment.
216 |
217 | ## Example 3: durable consumption goods
218 |
219 | We generate our third economy by raising $\phi_1$ further, to 1.0.
220 | We also raise the production function parameter from 0.1 to 0.15 (which
221 | raises the non-stochastic steady state value of capital above zero).
222 |
223 | We also change the specification of preferences to make the consumption
224 | good *durable*.
225 |
226 | Specifically, we allow for a single durable household good obeying:
227 |
228 | $$
229 | h_t = \delta_h h_{t-1} + c_t \, , 0<\delta_h<1
230 | $$
231 |
232 | Services are related to the stock of durables at the beginning of the
233 | period:
234 |
235 | $$
236 | s_t = \lambda h_{t-1} \, , \lambda > 0
237 | $$
238 |
239 | And preferences are ordered by:
240 |
241 | $$
242 | - \frac{1}{2} \mathbb{E} \sum_{t=0}^\infty \beta^t [(\lambda h_{t-1} - b_t)^2 + l_t^2]|J_0
243 | $$
244 |
245 | To implement this, we set $\lambda=0.1$ and $\pi = 0$ (we
246 | have already set $\theta_h = 1$ and $\delta_h = 0.9$).
247 |
248 | We start from an initial condition that makes consumption begin near
249 | around its non-stochastic steady state.
250 |
251 | ```{code-cell} python3
252 | ϕ_13 = 1
253 | ϕ_i3 = np.array([[1], [-ϕ_13]])
254 |
255 | γ_12 = 0.15
256 | γ_2 = np.array([[γ_12], [0]])
257 |
258 | l_λ2 = np.array([[0.1]])
259 | π_h2 = np.array([[0]])
260 |
261 | x01 = np.array([[150], [100], [1], [0], [0]])
262 |
263 | tech3 = (ϕ_c, ϕ_g, ϕ_i3, γ_2, δ_k, θ_k)
264 | pref2 = (β, l_λ2, π_h2, δ_h, θ_h)
265 |
266 | econ3 = DLE(info1, tech3, pref2)
267 | econ3.compute_sequence(x01, ts_length=300)
268 |
269 | # This is the right panel of Fig 5.11.1 from p.111 of HS2013
270 | plt.plot(econ3.c[0], label='Cons.')
271 | plt.plot(econ3.i[0], label='Inv.')
272 | plt.legend()
273 | plt.show()
274 | ```
275 |
276 | In contrast to Hall's original model of Example 1, it is now investment
277 | that is much smoother than consumption.
278 |
279 | This illustrates how making consumption goods durable tends to undo the
280 | strong consumption smoothing result that Hall obtained.
281 |
282 | ```{code-cell} python3
283 | econ3.irf(ts_length=40, shock=None)
284 | # This is the left panel of Fig 5.11.1 from p.111 of HS2013
285 | plt.plot(econ3.c_irf, label='Cons.')
286 | plt.plot(econ3.i_irf, label='Inv.')
287 | plt.legend()
288 | plt.show()
289 | ```
290 |
291 | The impulse response functions confirm that consumption is now much more
292 | responsive to an endowment shock (and investment less so) than in
293 | Example 1.
294 |
295 | As in Example 2, the endowment shock has permanent effects on
296 | neither variable.
297 |
--------------------------------------------------------------------------------
/lectures/_static/lecture_specific/amss/recursive_allocation.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | from numba import njit, prange
3 | from quantecon import optimize
4 |
5 | @njit
6 | def get_grid_nodes(grid):
7 | """
8 | Get the actual grid points from a grid tuple.
9 | """
10 | x_min, x_max, x_num = grid
11 | return np.linspace(x_min, x_max, x_num)
12 |
13 | @njit
14 | def linear_interp_1d_scalar(x_min, x_max, x_num, y_values, x_val):
15 | """Helper function for scalar interpolation"""
16 | x_nodes = np.linspace(x_min, x_max, x_num)
17 |
18 | # Extrapolation with linear extension
19 | if x_val <= x_nodes[0]:
20 | # Linear extrapolation using first two points
21 | if x_num >= 2:
22 | slope = (y_values[1] - y_values[0]) / (x_nodes[1] - x_nodes[0])
23 | return y_values[0] + slope * (x_val - x_nodes[0])
24 | else:
25 | return y_values[0]
26 |
27 | if x_val >= x_nodes[-1]:
28 | # Linear extrapolation using last two points
29 | if x_num >= 2:
30 | slope = (y_values[-1] - y_values[-2]) / (x_nodes[-1] - x_nodes[-2])
31 | return y_values[-1] + slope * (x_val - x_nodes[-1])
32 | else:
33 | return y_values[-1]
34 |
35 | # Binary search for the right interval
36 | left = 0
37 | right = x_num - 1
38 | while right - left > 1:
39 | mid = (left + right) // 2
40 | if x_nodes[mid] <= x_val:
41 | left = mid
42 | else:
43 | right = mid
44 |
45 | # Linear interpolation
46 | x_left = x_nodes[left]
47 | x_right = x_nodes[right]
48 | y_left = y_values[left]
49 | y_right = y_values[right]
50 |
51 | weight = (x_val - x_left) / (x_right - x_left)
52 | return y_left * (1 - weight) + y_right * weight
53 |
54 | @njit
55 | def linear_interp_1d(x_grid, y_values, x_query):
56 | """
57 | Perform 1D linear interpolation.
58 | """
59 | x_min, x_max, x_num = x_grid
60 | return linear_interp_1d_scalar(x_min, x_max, x_num, y_values, x_query[0])
61 |
62 | class AMSS:
63 | # WARNING: THE CODE IS EXTREMELY SENSITIVE TO CHOCIES OF PARAMETERS.
64 | # DO NOT CHANGE THE PARAMETERS AND EXPECT IT TO WORK
65 |
66 | def __init__(self, pref, β, Π, g, x_grid, bounds_v):
67 | self.β, self.Π, self.g = β, Π, g
68 | self.x_grid = x_grid
69 | self.n = x_grid[0][2]
70 | self.S = len(Π)
71 | self.bounds = bounds_v
72 | self.pref = pref
73 |
74 | self.T_v, self.T_w = bellman_operator_factory(Π, β, x_grid, g,
75 | bounds_v)
76 |
77 | self.V_solved = False
78 | self.W_solved = False
79 |
80 | def compute_V(self, V, σ_v_star, tol_vfi, maxitr, print_itr):
81 |
82 | T_v = self.T_v
83 |
84 | self.success = False
85 |
86 | V_new = np.zeros_like(V)
87 |
88 | Δ = 1.0
89 | for itr in range(maxitr):
90 | T_v(V, V_new, σ_v_star, self.pref)
91 |
92 | Δ = np.max(np.abs(V_new - V))
93 |
94 | if Δ < tol_vfi:
95 | self.V_solved = True
96 | print('Successfully completed VFI after %i iterations'
97 | % (itr+1))
98 | break
99 |
100 | if (itr + 1) % print_itr == 0:
101 | print('Error at iteration %i : ' % (itr + 1), Δ)
102 |
103 | V[:] = V_new[:]
104 |
105 | self.V = V
106 | self.σ_v_star = σ_v_star
107 |
108 | return V, σ_v_star
109 |
110 | def compute_W(self, b_0, W, σ_w_star):
111 | T_w = self.T_w
112 | V = self.V
113 |
114 | T_w(W, σ_w_star, V, b_0, self.pref)
115 |
116 | self.W = W
117 | self.σ_w_star = σ_w_star
118 | self.W_solved = True
119 | print('Succesfully solved the time 0 problem.')
120 |
121 | return W, σ_w_star
122 |
123 | def solve(self, V, σ_v_star, b_0, W, σ_w_star, tol_vfi=1e-7,
124 | maxitr=1000, print_itr=10):
125 | print("===============")
126 | print("Solve time 1 problem")
127 | print("===============")
128 | self.compute_V(V, σ_v_star, tol_vfi, maxitr, print_itr)
129 | print("===============")
130 | print("Solve time 0 problem")
131 | print("===============")
132 | self.compute_W(b_0, W, σ_w_star)
133 |
134 | def simulate(self, s_hist, b_0):
135 | if not (self.V_solved and self.W_solved):
136 | msg = "V and W need to be successfully computed before simulation."
137 | raise ValueError(msg)
138 |
139 | pref = self.pref
140 | x_grid, g, β, S = self.x_grid, self.g, self.β, self.S
141 | σ_v_star, σ_w_star = self.σ_v_star, self.σ_w_star
142 | Π = self.Π
143 |
144 | # Extract the grid tuple from the list
145 | grid_tuple = x_grid[0] if isinstance(x_grid, list) else x_grid
146 |
147 | T = len(s_hist)
148 | s_0 = s_hist[0]
149 |
150 | # Pre-allocate
151 | n_hist = np.zeros(T)
152 | x_hist = np.zeros(T)
153 | c_hist = np.zeros(T)
154 | τ_hist = np.zeros(T)
155 | b_hist = np.zeros(T)
156 | g_hist = np.zeros(T)
157 |
158 | # Compute t = 0
159 | l_0, T_0 = σ_w_star[s_0]
160 | c_0 = (1 - l_0) - g[s_0]
161 | x_0 = (-pref.Uc(c_0, l_0) * (c_0 - T_0 - b_0) +
162 | pref.Ul(c_0, l_0) * (1 - l_0))
163 |
164 | n_hist[0] = (1 - l_0)
165 | x_hist[0] = x_0
166 | c_hist[0] = c_0
167 | τ_hist[0] = 1 - pref.Ul(c_0, l_0) / pref.Uc(c_0, l_0)
168 | b_hist[0] = b_0
169 | g_hist[0] = g[s_0]
170 |
171 | # Compute t > 0
172 | for t in range(T - 1):
173 | x_ = x_hist[t]
174 | s_ = s_hist[t]
175 | l = np.zeros(S)
176 | T = np.zeros(S)
177 | for s in range(S):
178 | x_arr = np.array([x_])
179 | l[s] = linear_interp_1d(grid_tuple, σ_v_star[s_, :, s], x_arr)
180 | T[s] = linear_interp_1d(grid_tuple, σ_v_star[s_, :, S+s], x_arr)
181 |
182 | c = (1 - l) - g
183 | u_c = pref.Uc(c, l)
184 | Eu_c = Π[s_] @ u_c
185 |
186 | x = u_c * x_ / (β * Eu_c) - u_c * (c - T) + pref.Ul(c, l) * (1 - l)
187 |
188 | c_next = c[s_hist[t+1]]
189 | l_next = l[s_hist[t+1]]
190 |
191 | x_hist[t+1] = x[s_hist[t+1]]
192 | n_hist[t+1] = 1 - l_next
193 | c_hist[t+1] = c_next
194 | τ_hist[t+1] = 1 - pref.Ul(c_next, l_next) / pref.Uc(c_next, l_next)
195 | b_hist[t+1] = x_ / (β * Eu_c)
196 | g_hist[t+1] = g[s_hist[t+1]]
197 |
198 | return c_hist, n_hist, b_hist, τ_hist, g_hist, n_hist
199 |
200 |
201 | def obj_factory(Π, β, x_grid, g):
202 | S = len(Π)
203 | # Extract the grid tuple from the list
204 | grid_tuple = x_grid[0] if isinstance(x_grid, list) else x_grid
205 |
206 | @njit
207 | def obj_V(σ, state, V, pref):
208 | # Unpack state
209 | s_, x_ = state
210 |
211 | l = σ[:S]
212 | T = σ[S:]
213 |
214 | c = (1 - l) - g
215 | u_c = pref.Uc(c, l)
216 | Eu_c = Π[s_] @ u_c
217 | x = u_c * x_ / (β * Eu_c) - u_c * (c - T) + pref.Ul(c, l) * (1 - l)
218 |
219 | V_next = np.zeros(S)
220 |
221 | for s in range(S):
222 | V_next[s] = linear_interp_1d(grid_tuple, V[s], np.array([x[s]]))
223 |
224 | out = Π[s_] @ (pref.U(c, l) + β * V_next)
225 |
226 | return out
227 |
228 | @njit
229 | def obj_W(σ, state, V, pref):
230 | # Unpack state
231 | s_, b_0 = state
232 | l, T = σ
233 |
234 | c = (1 - l) - g[s_]
235 | x = -pref.Uc(c, l) * (c - T - b_0) + pref.Ul(c, l) * (1 - l)
236 |
237 | V_next = linear_interp_1d(grid_tuple, V[s_], np.array([x]))
238 |
239 | out = pref.U(c, l) + β * V_next
240 |
241 | return out
242 |
243 | return obj_V, obj_W
244 |
245 |
246 | def bellman_operator_factory(Π, β, x_grid, g, bounds_v):
247 | obj_V, obj_W = obj_factory(Π, β, x_grid, g)
248 | # Extract the grid tuple from the list
249 | grid_tuple = x_grid[0] if isinstance(x_grid, list) else x_grid
250 | n = grid_tuple[2]
251 | S = len(Π)
252 | x_nodes = get_grid_nodes(grid_tuple)
253 |
254 | @njit(parallel=True)
255 | def T_v(V, V_new, σ_star, pref):
256 | for s_ in prange(S):
257 | for x_i in prange(n):
258 | state = (s_, x_nodes[x_i])
259 | x0 = σ_star[s_, x_i]
260 | res = optimize.nelder_mead(obj_V, x0, bounds=bounds_v,
261 | args=(state, V, pref))
262 |
263 | if res.success:
264 | V_new[s_, x_i] = res.fun
265 | σ_star[s_, x_i] = res.x
266 | else:
267 | print("Optimization routine failed.")
268 |
269 | bounds_w = np.array([[-9.0, 1.0], [0., 10.]])
270 |
271 | def T_w(W, σ_star, V, b_0, pref):
272 | for s_ in prange(S):
273 | state = (s_, b_0)
274 | x0 = σ_star[s_]
275 | res = optimize.nelder_mead(obj_W, x0, bounds=bounds_w,
276 | args=(state, V, pref))
277 |
278 | W[s_] = res.fun
279 | σ_star[s_] = res.x
280 |
281 | return T_v, T_w
--------------------------------------------------------------------------------
/lectures/permanent_income_dles.md:
--------------------------------------------------------------------------------
1 | ---
2 | jupytext:
3 | text_representation:
4 | extension: .md
5 | format_name: myst
6 | kernelspec:
7 | display_name: Python 3
8 | language: python
9 | name: python3
10 | ---
11 |
12 | (permanent_income_dles)=
13 | ```{raw} html
14 |
19 | ```
20 |
21 | ```{index} single: python
22 | ```
23 |
24 | # Permanent Income Model using the DLE Class
25 |
26 | This lecture is part of a suite of lectures that use the quantecon DLE class to instantiate models within the
27 | {cite}`HS2013` class of models described in detail in {doc}`Recursive Models of Dynamic Linear Economies `.
28 |
29 | In addition to what's included in Anaconda, this lecture uses the quantecon library.
30 |
31 | ```{code-cell} ipython
32 | ---
33 | tags: [hide-output]
34 | ---
35 | !pip install --upgrade quantecon
36 | ```
37 |
38 | This lecture adds a third solution method for the
39 | linear-quadratic-Gaussian permanent income model with
40 | $\beta R = 1$, complementing the other two solution methods described in [Optimal Savings I: The Permanent Income Model](https://python-intro.quantecon.org/perm_income.html) and
41 | [Optimal Savings II: LQ Techniques](https://python-intro.quantecon.org/perm_income_cons.html) and [this Jupyter
42 | notebook](http://nbviewer.jupyter.org/github/QuantEcon/QuantEcon.notebooks/blob/master/permanent_income.ipynb).
43 |
44 | The additional solution method uses the **DLE** class.
45 |
46 | In this way, we map the permanent
47 | income model into the framework of Hansen & Sargent (2013) "Recursive
48 | Models of Dynamic Linear Economies" {cite}`HS2013`.
49 |
50 | We'll also require the following imports
51 |
52 | ```{code-cell} ipython
53 | import numpy as np
54 | import matplotlib.pyplot as plt
55 | from quantecon import DLE
56 |
57 | np.set_printoptions(suppress=True, precision=4)
58 | ```
59 |
60 | ## The permanent income model
61 |
62 | The LQ permanent income model is an example of a **savings problem**.
63 |
64 | A consumer has preferences over consumption streams that are ordered by
65 | the utility functional
66 |
67 | ```{math}
68 | :label: perm-utility
69 |
70 | E_0 \sum_{t=0}^\infty \beta^t u(c_t)
71 | ```
72 |
73 | where $E_t$ is the mathematical expectation conditioned on the
74 | consumer's time $t$ information, $c_t$ is time $t$
75 | consumption, $u(c)$ is a strictly concave one-period utility
76 | function, and $\beta \in (0,1)$ is a discount factor.
77 |
78 | The LQ model gets its name partly from assuming that the utility
79 | function $u$ is quadratic:
80 |
81 | $$
82 | u(c) = -.5(c - \gamma)^2
83 | $$
84 |
85 | where $\gamma>0$ is a bliss level of consumption.
86 |
87 | The consumer maximizes the utility functional {eq}`perm-utility` by choosing a
88 | consumption, borrowing plan $\{c_t, b_{t+1}\}_{t=0}^\infty$
89 | subject to the sequence of budget constraints
90 |
91 | ```{math}
92 | :label: max-utility
93 |
94 | c_t + b_t = R^{-1} b_{t+1} + y_t, t \geq 0
95 | ```
96 |
97 | where $y_t$ is an exogenous stationary endowment process,
98 | $R$ is a constant gross risk-free interest rate, $b_t$ is
99 | one-period risk-free debt maturing at $t$, and $b_0$ is a
100 | given initial condition.
101 |
102 | We shall assume that $R^{-1} = \beta$.
103 |
104 | Equation {eq}`max-utility` is linear.
105 |
106 | We use another set of linear equations to model the endowment process.
107 |
108 | In particular, we assume that the endowment process has the state-space
109 | representation
110 |
111 | ```{math}
112 | :label: endowment
113 |
114 | \begin{aligned} z_{t+1} & = A_{22} z_t + C_2 w_{t+1} \cr
115 | y_t & = U_y z_t \cr \end{aligned}
116 | ```
117 |
118 | where $w_{t+1}$ is an IID process with mean zero and identity
119 | contemporaneous covariance matrix, $A_{22}$ is a stable matrix,
120 | its eigenvalues being strictly below unity in modulus, and $U_y$
121 | is a selection vector that identifies $y$ with a particular linear
122 | combination of the $z_t$.
123 |
124 | We impose the following condition on the consumption, borrowing plan:
125 |
126 | ```{math}
127 | :label: contraint
128 |
129 | E_0 \sum_{t=0}^\infty \beta^t b_t^2 < +\infty
130 | ```
131 |
132 | This condition suffices to rule out Ponzi schemes.
133 |
134 | (We impose this condition to rule out a borrow-more-and-more plan that
135 | would allow the household to enjoy bliss consumption forever)
136 |
137 | The state vector confronting the household at $t$ is
138 |
139 | $$
140 | x_t = \begin{bmatrix} z_t \\ b_t \end{bmatrix}
141 | $$
142 |
143 | where $b_t$ is its one-period debt falling due at the beginning of
144 | period $t$ and $z_t$ contains all variables useful for
145 | forecasting its future endowment.
146 |
147 | We assume that $\{y_t\}$ follows a second order univariate
148 | autoregressive process:
149 |
150 | $$
151 | y_{t+1} = \alpha + \rho_1 y_t + \rho_2 y_{t-1} + \sigma w_{t+1}
152 | $$
153 |
154 | ### Solution with the DLE class
155 |
156 | One way of solving this model is to map the problem into the framework
157 | outlined in Section 4.8 of {cite}`HS2013` by setting up our technology,
158 | information and preference matrices as follows:
159 |
160 | **Technology:**
161 | $\phi_c= \left[ {\begin{array}{c} 1 \\ 0 \end{array} } \right]$
162 | ,
163 | $\phi_g= \left[ {\begin{array}{c} 0 \\ 1 \end{array} } \right]$
164 | ,
165 | $\phi_i= \left[ {\begin{array}{c} -1 \\ -0.00001 \end{array} } \right]$,
166 | $\Gamma= \left[ {\begin{array}{c} -1 \\ 0 \end{array} } \right]$,
167 | $\Delta_k = 0$, $\Theta_k = R$.
168 |
169 | **Information:**
170 | $A_{22} = \left[ {\begin{array}{ccc} 1 & 0 & 0 \\ \alpha & \rho_1 & \rho_2 \\ 0 & 1 & 0 \end{array} } \right]$,
171 | $C_{2} = \left[ {\begin{array}{c} 0 \\ \sigma \\ 0 \end{array} } \right]$,
172 | $U_b = \left[ {\begin{array}{ccc} \gamma & 0 & 0 \end{array} } \right]$,
173 | $U_d = \left[ {\begin{array}{ccc} 0 & 1 & 0 \\ 0 & 0 & 0 \end{array} } \right]$.
174 |
175 | **Preferences:** $\Lambda = 0$, $\Pi = 1$,
176 | $\Delta_h = 0$, $\Theta_h = 0$.
177 |
178 | We set parameters
179 |
180 | $\alpha = 10, \beta = 0.95, \rho_1 = 0.9, \rho_2 = 0, \sigma = 1$
181 |
182 | (The value of $\gamma$ does not affect the optimal decision rule)
183 |
184 | The chosen matrices mean that the household's technology is:
185 |
186 | $$
187 | c_t + k_{t-1} = i_t + y_t
188 | $$
189 |
190 | $$
191 | \frac{k_t}{R} = i_t
192 | $$
193 |
194 | $$
195 | l_t^2 = (0.00001)^2i_t
196 | $$
197 |
198 | Combining the first two of these gives the budget constraint of the
199 | permanent income model, where $k_t = b_{t+1}$.
200 |
201 | The third equation is a very small penalty on debt-accumulation to rule
202 | out Ponzi schemes.
203 |
204 | We set up this instance of the DLE class below:
205 |
206 | ```{code-cell} python3
207 | α, β, ρ_1, ρ_2, σ = 10, 0.95, 0.9, 0, 1
208 |
209 | γ = np.array([[-1], [0]])
210 | ϕ_c = np.array([[1], [0]])
211 | ϕ_g = np.array([[0], [1]])
212 | ϕ_1 = 1e-5
213 | ϕ_i = np.array([[-1], [-ϕ_1]])
214 | δ_k = np.array([[0]])
215 | θ_k = np.array([[1 / β]])
216 | β = np.array([[β]])
217 | l_λ = np.array([[0]])
218 | π_h = np.array([[1]])
219 | δ_h = np.array([[0]])
220 | θ_h = np.array([[0]])
221 |
222 | a22 = np.array([[1, 0, 0],
223 | [α, ρ_1, ρ_2],
224 | [0, 1, 0]])
225 |
226 | c2 = np.array([[0], [σ], [0]])
227 | ud = np.array([[0, 1, 0],
228 | [0, 0, 0]])
229 | ub = np.array([[100, 0, 0]])
230 |
231 | x0 = np.array([[0], [0], [1], [0], [0]])
232 |
233 | info1 = (a22, c2, ub, ud)
234 | tech1 = (ϕ_c, ϕ_g, ϕ_i, γ, δ_k, θ_k)
235 | pref1 = (β, l_λ, π_h, δ_h, θ_h)
236 | econ1 = DLE(info1, tech1, pref1)
237 | ```
238 |
239 | To check the solution of this model with that from the **LQ** problem,
240 | we select the $S_c$ matrix from the DLE class.
241 |
242 | The solution to the
243 | DLE economy has:
244 |
245 | $$
246 | c_t = S_c x_t
247 | $$
248 |
249 | ```{code-cell} python3
250 | econ1.Sc
251 | ```
252 |
253 | The state vector in the DLE class is:
254 |
255 | $$
256 | x_t = \left[ {\begin{array}{c}
257 | h_{t-1} \\ k_{t-1} \\ z_t
258 | \end{array} }
259 | \right]
260 | $$
261 |
262 | where $k_{t-1}$ = $b_{t}$ is set up to be $b_t$ in the
263 | permanent income model.
264 |
265 | The state vector in the LQ problem is
266 | $\begin{bmatrix} z_t \\ b_t \end{bmatrix}$.
267 |
268 | Consequently, the relevant elements of `econ1.Sc` are the same as in
269 | $-F$ occur when we apply other approaches to the same model in the lecture
270 | [Optimal Savings II: LQ Techniques](https://python-intro.quantecon.org/perm_income_cons.html) and [this Jupyter
271 | notebook](http://nbviewer.jupyter.org/github/QuantEcon/QuantEcon.notebooks/blob/master/permanent_income.ipynb).
272 |
273 | The plot below quickly replicates the first two figures of
274 | that lecture and that notebook to confirm that the solutions are the same
275 |
276 | ```{code-cell} python3
277 | fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 4))
278 |
279 | for i in range(25):
280 | econ1.compute_sequence(x0, ts_length=150)
281 | ax1.plot(econ1.c[0], c='g')
282 | ax1.plot(econ1.d[0], c='b')
283 | ax1.plot(econ1.c[0], label='Consumption', c='g')
284 | ax1.plot(econ1.d[0], label='Income', c='b')
285 | ax1.legend()
286 |
287 | for i in range(25):
288 | econ1.compute_sequence(x0, ts_length=150)
289 | ax2.plot(econ1.k[0], color='r')
290 | ax2.plot(econ1.k[0], label='Debt', c='r')
291 | ax2.legend()
292 | plt.show()
293 | ```
294 |
--------------------------------------------------------------------------------
/lectures/tax_smoothing_3.md:
--------------------------------------------------------------------------------
1 | ---
2 | jupytext:
3 | text_representation:
4 | extension: .md
5 | format_name: myst
6 | kernelspec:
7 | display_name: Python 3
8 | language: python
9 | name: python3
10 | ---
11 |
12 | (tax_smoothing_3)=
13 | ```{raw} html
14 |
19 | ```
20 |
21 | ```{index} single: python
22 | ```
23 |
24 | # How to Pay for a War: Part 3
25 |
26 |
27 | ## Overview
28 |
29 | This lecture presents another application of Markov jump linear quadratic dynamic programming and constitutes a {doc}`sequel to an earlier lecture `.
30 |
31 | We again use a method introduced in lecture {doc}`Markov Jump LQ dynamic programming `
32 | to implement some ideas of {cite}`barro1999determinants` and {cite}`barro2003religion`) that
33 | extend the classic {cite}`Barro1979` model of tax smoothing.
34 |
35 | {cite}`Barro1979` is about a government that borrows and lends in order
36 | to help it minimize an intertemporal measure of distortions caused by
37 | taxes.
38 |
39 | Technically, {cite}`Barro1979` looks a lot like a consumption-smoothing model.
40 |
41 | Our generalization will also look
42 | like a souped-up consumption-smoothing model.
43 |
44 | In this lecture, we describe a tax-smoothing problem of a
45 | government that faces **roll-over risk**.
46 |
47 |
48 | In addition to what's in Anaconda, this lecture deploys the quantecon library:
49 |
50 | ```{code-cell} ipython
51 | ---
52 | tags: [hide-output]
53 | ---
54 | !pip install --upgrade quantecon
55 | ```
56 |
57 | Let's start with some standard imports:
58 |
59 | ```{code-cell} ipython
60 | import quantecon as qe
61 | import numpy as np
62 | import matplotlib.pyplot as plt
63 | ```
64 |
65 | ## Roll-over risk
66 |
67 | Let $T_t$ denote tax collections, $\beta$ a discount factor,
68 | $b_{t,t+1}$ time $t+1$ goods that the government promises to
69 | pay at $t$, $G_t$ government purchases, $p^t_{t+1}$
70 | the number of time $t$ goods received per time $t+1$ goods
71 | promised.
72 |
73 | The stochastic process of government expenditures is
74 | exogenous.
75 |
76 | The government’s problem is to choose a plan for borrowing
77 | and tax collections $\{b_{t+1}, T_t\}_{t=0}^\infty$ to minimize
78 |
79 | $$
80 | E_0 \sum_{t=0}^\infty \beta^t T_t^2
81 | $$
82 |
83 | subject to the constraints
84 |
85 | $$
86 | T_t + p^t_{t+1} b_{t,t+1} = G_t + b_{t-1,t}
87 | $$
88 |
89 | $$
90 | G_t = U_{g,t} z_t
91 | $$
92 |
93 | $$
94 | z_{t+1} = A_{22,t} z_t + C_{2,t} w_{t+1}
95 | $$
96 |
97 | where $w_{t+1} \sim {\cal N}(0,I)$.
98 |
99 | Let
100 | * $T_t, b_{t, t+1}$ be *controls* chosen at $t$
101 | * $b_{t-1,t}$ be an endogenous state variable inherited from
102 | the past at time $t$
103 | * $p^t_{t+1}$ be an exogenous price at time $t$.
104 |
105 | This is the same set-up as used {doc}`in this lecture `.
106 |
107 | We will consider a situation in which the government faces “roll-over
108 | risk”.
109 |
110 | Specifically, we shut down the government’s ability to borrow in
111 | one of the Markov states.
112 |
113 | ## A dead end
114 |
115 | A first thought for how to implement this might be to allow
116 | $p^t_{t+1}$ to vary over time with:
117 |
118 | $$
119 | p^t_{t+1} = \beta
120 | $$
121 |
122 | in Markov state 1 and
123 |
124 | $$
125 | p^t_{t+1} = 0
126 | $$
127 |
128 | in Markov state 2.
129 |
130 | Consequently, in the second Markov state, the
131 | government is unable to borrow, and the budget constraint becomes
132 | $T_t = G_t + b_{t-1,t}$.
133 |
134 | However, if this is the only adjustment we make in our linear-quadratic
135 | model, the government will not set $b_{t,t+1} = 0$, which is the
136 | outcome we want to express *roll-over* risk in period $t$.
137 |
138 | Instead, the government would have an incentive to set $b_{t,t+1}$
139 | to a large negative number in state 2 – it would accumulate large
140 | amounts of *assets* to bring into period $t+1$ because that is
141 | cheap
142 |
143 | * Riccati equations will tell us this
144 |
145 | Thus, we must represent “roll-over risk” some other way.
146 |
147 | ## Better representation of roll-over risk
148 |
149 | To force the government to set $b_{t,t+1} = 0$, we can instead
150 | extend the model to have four Markov states:
151 |
152 | 1. Good today, good yesterday
153 | 1. Good today, bad yesterday
154 | 1. Bad today, good yesterday
155 | 1. Bad today, bad yesterday
156 |
157 | where good is a state in which effectively the government can issue debt
158 | and bad is a state in which effectively the government can’t issue debt.
159 |
160 | We’ll explain what *effectively* means shortly.
161 |
162 | We now set
163 |
164 | $$
165 | p^t_{t+1} = \beta
166 | $$
167 |
168 | in all states.
169 |
170 | In addition – and this is important because it defines what we mean by
171 | *effectively* -- we put a large penalty on the $b_{t-1,t}$
172 | element of the state vector in states 2 and 4.
173 |
174 | This will prevent the
175 | government from wishing to issue any debt in states 3 or 4 because it
176 | would experience a large penalty from doing so in the next period.
177 |
178 | The transition matrix for this formulation is:
179 |
180 | $$
181 | \Pi = \begin{bmatrix} 0.95 & 0 & 0.05 & 0 \\
182 | 0.95 & 0 & 0.05 & 0 \\
183 | 0 & 0.9 & 0 & 0.1 \\
184 | 0 & 0.9 & 0 & 0.1 \\
185 | \end{bmatrix}
186 | $$
187 |
188 | This transition matrix ensures that the Markov state cannot move, for
189 | example, from state 3 to state 1.
190 |
191 | Because state 3 is “bad today”, the next period cannot have “good yesterday”.
192 |
193 | ```{code-cell} python3
194 | # Model parameters
195 | β, Gbar, ρ, σ = 0.95, 5, 0.8, 1
196 |
197 | # Basic model matrices
198 | A22 = np.array([[1, 0], [Gbar, ρ], ])
199 | C2 = np.array([[0], [σ]])
200 | Ug = np.array([[0, 1]])
201 |
202 | # LQ framework matrices
203 | A_t = np.zeros((1, 3))
204 | A_b = np.hstack((np.zeros((2, 1)), A22))
205 | A = np.vstack((A_t, A_b))
206 |
207 | B = np.zeros((3, 1))
208 | B[0, 0] = 1
209 |
210 | C = np.vstack((np.zeros((1, 1)), C2))
211 |
212 | Sg = np.hstack((np.zeros((1, 1)), Ug))
213 | S1 = np.zeros((1, 3))
214 | S1[0, 0] = 1
215 | S = S1 + Sg
216 |
217 | R = S.T @ S
218 |
219 | # Large penalty on debt in R2 to prevent borrowing in a bad state
220 | R1 = np.copy(R)
221 | R2 = np.copy(R)
222 | R1[0, 0] = R[0, 0] + 1e-9
223 | R2[0, 0] = R[0, 0] + 1e12
224 |
225 | M = np.array([[-β]])
226 | Q = M.T @ M
227 | W = M.T @ S
228 |
229 | Π = np.array([[0.95, 0, 0.05, 0],
230 | [0.95, 0, 0.05, 0],
231 | [0, 0.9, 0, 0.1],
232 | [0, 0.9, 0, 0.1]])
233 |
234 | # Construct lists of matrices that correspond to each state
235 | As = [A, A, A, A]
236 | Bs = [B, B, B, B]
237 | Cs = [C, C, C, C]
238 | Rs = [R1, R2, R1, R2]
239 | Qs = [Q, Q, Q, Q]
240 | Ws = [W, W, W, W]
241 |
242 | lqm = qe.LQMarkov(Π, Qs, Rs, As, Bs, Cs=Cs, Ns=Ws, beta=β)
243 | lqm.stationary_values();
244 | ```
245 |
246 | Using the same process for $G_t$ as
247 | in {doc}`this lecture `, we shall simulate our model with roll-over risk.
248 |
249 | When $p^t_{t+1} = \beta$
250 | government debt fluctuates around zero.
251 |
252 | The spikes in the tax collection series indicate periods when the government is unable to access financial
253 | markets:
254 | * positive spikes occur when debt is positive and the government
255 | must urgently raise tax revenues now
256 |
257 | Negative spikes occur when the government has positive asset holdings.
258 |
259 | An inability to use financial markets in the next period means that the
260 | government uses those assets to lower taxation today.
261 |
262 | ```{code-cell} python3
263 | x0 = np.array([[0, 1, 25]])
264 | T = 300
265 | x, u, w, state = lqm.compute_sequence(x0, ts_length=T)
266 |
267 | # Calculate taxation each period from the budget constraint and the Markov state
268 | tax = np.zeros([T, 1])
269 | for i in range(T):
270 | tax[i, :] = S @ x[:, i] + M @ u[:, i]
271 |
272 | # Plot of debt issuance and taxation
273 | fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 3))
274 | ax1.plot(x[0, :])
275 | ax1.set_title('One-period debt issuance')
276 | ax1.set_xlabel('Time')
277 | ax2.plot(tax)
278 | ax2.set_title('Taxation')
279 | ax2.set_xlabel('Time')
280 | plt.show()
281 | ```
282 |
283 | We can adjust parameters so that, rather than debt fluctuating
284 | around zero, the government is a debtor in every period that it can borrow.
285 |
286 |
287 | To accomplish this, we simply raise $p^t_{t+1}$ to
288 | $\beta + 0.02 = 0.97$.
289 |
290 | ```{code-cell} python3
291 | M = np.array([[-β - 0.02]])
292 |
293 | Q = M.T @ M
294 | W = M.T @ S
295 |
296 | # Construct lists of matrices
297 | As = [A, A, A, A]
298 | Bs = [B, B, B, B]
299 | Cs = [C, C, C, C]
300 | Rs = [R1, R2, R1, R2]
301 | Qs = [Q, Q, Q, Q]
302 | Ws = [W, W, W, W]
303 |
304 | lqm2 = qe.LQMarkov(Π, Qs, Rs, As, Bs, Cs=Cs, Ns=Ws, beta=β)
305 | x, u, w, state = lqm2.compute_sequence(x0, ts_length=T)
306 |
307 | # Calculate taxation each period from the budget constraint and the
308 | # Markov state
309 | tax = np.zeros([T, 1])
310 | for i in range(T):
311 | tax[i, :] = S @ x[:, i] + M @ u[:, i]
312 |
313 | # Plot of debt issuance and taxation
314 | fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 3))
315 | ax1.plot(x[0, :])
316 | ax1.set_title('One-period debt issuance')
317 | ax1.set_xlabel('Time')
318 | ax2.plot(tax)
319 | ax2.set_title('Taxation')
320 | ax2.set_xlabel('Time')
321 | plt.show()
322 | ```
323 |
324 | With a lower interest rate, the government has an incentive to
325 | increase debt over time.
326 |
327 | However, with “roll-over risk”, debt is
328 | recurrently reset to zero and tax collections spike up.
329 |
330 | In this model, high costs of a “sudden stop” make the government wary about letting its debt get too high.
331 |
332 |
--------------------------------------------------------------------------------
/lectures/rosen_schooling_model.md:
--------------------------------------------------------------------------------
1 | ---
2 | jupytext:
3 | text_representation:
4 | extension: .md
5 | format_name: myst
6 | kernelspec:
7 | display_name: Python 3
8 | language: python
9 | name: python3
10 | ---
11 |
12 | (rosen_schooling_model)=
13 | ```{raw} html
14 |
19 | ```
20 |
21 | ```{index} single: python
22 | ```
23 |
24 | # Rosen Schooling Model
25 |
26 | This lecture is yet another part of a suite of lectures that use the quantecon DLE class to instantiate models within the
27 | {cite}`HS2013` class of models described in detail in {doc}`Recursive Models of Dynamic Linear Economies `.
28 |
29 | In addition to what's included in Anaconda, this lecture uses the quantecon library
30 |
31 | ```{code-cell} ipython
32 | ---
33 | tags: [hide-output]
34 | ---
35 | !pip install --upgrade quantecon
36 | ```
37 |
38 | We'll also need the following imports:
39 |
40 | ```{code-cell} ipython
41 | import numpy as np
42 | import matplotlib.pyplot as plt
43 | from collections import namedtuple
44 | from quantecon import DLE
45 | ```
46 |
47 | ## A one-occupation model
48 |
49 | Ryoo and Rosen's (2004) {cite}`ryoo2004engineering` partial equilibrium model determines
50 |
51 | - a stock of "Engineers" $N_t$
52 | - a number of new entrants in engineering school, $n_t$
53 | - the wage rate of engineers, $w_t$
54 |
55 | It takes k periods of schooling to become an engineer.
56 |
57 | The model consists of the following equations:
58 |
59 | - a demand curve for engineers:
60 |
61 | $$
62 | w_t = - \alpha_d N_t + \epsilon_{dt}
63 | $$
64 |
65 | - a time-to-build structure of the education process:
66 |
67 | $$
68 | N_{t+k} = \delta_N N_{t+k-1} + n_t
69 | $$
70 |
71 | - a definition of the discounted present value of each new engineering
72 | student:
73 |
74 | $$
75 | v_t = \beta_k \mathbb{E} \sum_{j=0}^\infty (\beta \delta_N)^j w_{t+k+j}
76 | $$
77 |
78 | - a supply curve of new students driven by present value $v_t$:
79 |
80 | $$
81 | n_t = \alpha_s v_t + \epsilon_{st}
82 | $$
83 |
84 | ## Mapping into HS2013 framework
85 |
86 | We represent this model in the {cite}`HS2013` framework by
87 |
88 | - sweeping the time-to-build structure and the demand for engineers
89 | into the household technology, and
90 | - putting the supply of engineers into the technology for producing
91 | goods
92 |
93 | ### Preferences
94 |
95 | $$
96 | \Pi = 0, \Lambda=
97 | \begin{bmatrix}
98 | \alpha_d & 0 & \cdots & 0
99 | \end{bmatrix}
100 | , \Delta_h =
101 | \begin{bmatrix}
102 | \delta_N & 1 & 0 & \cdots & 0 \\ 0 & 0 & 1 & \cdots & 0 \\ \vdots & \vdots & \vdots & \ddots & \vdots \\ 0 & \cdots& \cdots & 0 & 1 \\ 0 & 0 & 0 & \cdots & 0
103 | \end{bmatrix}
104 | , \Theta_h =
105 | \begin{bmatrix}
106 | 0 \\ 0 \\ \vdots \\ 0 \\ 1
107 | \end{bmatrix}
108 | $$
109 |
110 | where $\Lambda$ is a k+1 x 1 matrix, $\Delta_h$ is a k_1 x
111 | k+1 matrix, and $\Theta_h$ is a k+1 x 1 matrix.
112 |
113 | This specification sets $N_t = h_{1t-1}$, $n_t = c_t$,
114 | $h_{\tau+1,t-1} = n_{t-(k-\tau)}$ for $\tau = 1,...,k$.
115 |
116 | Below we set things up so that the number of years of education, $k$, can
117 | be varied.
118 |
119 | ### Technology
120 |
121 | To capture Ryoo and Rosen's {cite}`ryoo2004engineering` supply curve, we use the physical
122 | technology:
123 |
124 | $$
125 | c_t = i_t + d_{1t}
126 | $$
127 |
128 | $$
129 | \psi_1i_t = g_t
130 | $$
131 |
132 | where $\psi_1$ is inversely proportional to $\alpha_s$.
133 |
134 | ### Information
135 |
136 | Because we want $b_t = \epsilon_{dt}$ and $d_{1t} =\epsilon_{st}$, we set
137 |
138 | $$
139 | A_{22}=
140 | \begin{bmatrix}
141 | 1 & 0 & 0 \\ 0 & \rho_s & 0 \\ 0 & 0 & \rho_d
142 | \end{bmatrix}
143 | , C_2 =
144 | \begin{bmatrix}
145 | 0 & 0 \\ 1 & 0 \\ 0 & 1
146 | \end{bmatrix}
147 | , U_b =
148 | \begin{bmatrix}
149 | 30 & 0 & 1
150 | \end{bmatrix}
151 | , U_d =
152 | \begin{bmatrix}
153 | 10 & 1 & 0 \\ 0 & 0 & 0
154 | \end{bmatrix}
155 | $$
156 |
157 | where $\rho_s$ and $\rho_d$ describe the persistence of the
158 | supply and demand shocks
159 |
160 | ```{code-cell} python3
161 | Information = namedtuple('Information', ['a22', 'c2','ub','ud'])
162 | Technology = namedtuple('Technology', ['ϕ_c', 'ϕ_g', 'ϕ_i', 'γ', 'δ_k', 'θ_k'])
163 | Preferences = namedtuple('Preferences', ['β', 'l_λ', 'π_h', 'δ_h', 'θ_h'])
164 | ```
165 |
166 | ### Effects of changes in education technology and demand
167 |
168 | We now study how changing
169 |
170 | - the number of years of education required to become an engineer and
171 | - the slope of the demand curve
172 |
173 | affects responses to demand shocks.
174 |
175 | To begin, we set $k = 4$ and $\alpha_d = 0.1$
176 |
177 | ```{code-cell} python3
178 | k = 4 # Number of periods of schooling required to become an engineer
179 |
180 | β = np.array([[1 / 1.05]])
181 | α_d = np.array([[0.1]])
182 | α_s = 1
183 | ε_1 = 1e-7
184 | λ_1 = np.full((1, k), ε_1)
185 | # Use of ε_1 is trick to aquire detectability, see HS2013 p. 228 footnote 4
186 | l_λ = np.hstack((α_d, λ_1))
187 | π_h = np.array([[0]])
188 |
189 | δ_n = np.array([[0.95]])
190 | d1 = np.vstack((δ_n, np.zeros((k - 1, 1))))
191 | d2 = np.hstack((d1, np.eye(k)))
192 | δ_h = np.vstack((d2, np.zeros((1, k + 1))))
193 |
194 | θ_h = np.vstack((np.zeros((k, 1)),
195 | np.ones((1, 1))))
196 |
197 | ψ_1 = 1 / α_s
198 |
199 | ϕ_c = np.array([[1], [0]])
200 | ϕ_g = np.array([[0], [-1]])
201 | ϕ_i = np.array([[-1], [ψ_1]])
202 | γ = np.array([[0], [0]])
203 |
204 | δ_k = np.array([[0]])
205 | θ_k = np.array([[0]])
206 |
207 | ρ_s = 0.8
208 | ρ_d = 0.8
209 |
210 | a22 = np.array([[1, 0, 0],
211 | [0, ρ_s, 0],
212 | [0, 0, ρ_d]])
213 |
214 | c2 = np.array([[0, 0], [10, 0], [0, 10]])
215 | ub = np.array([[30, 0, 1]])
216 | ud = np.array([[10, 1, 0], [0, 0, 0]])
217 |
218 | info1 = Information(a22, c2, ub, ud)
219 | tech1 = Technology(ϕ_c, ϕ_g, ϕ_i, γ, δ_k, θ_k)
220 | pref1 = Preferences(β, l_λ, π_h, δ_h, θ_h)
221 |
222 | econ1 = DLE(info1, tech1, pref1)
223 | ```
224 |
225 | We create three other instances by:
226 |
227 | 1. Raising $\alpha_d$ to 2
228 | 1. Raising $k$ to 7
229 | 1. Raising $k$ to 10
230 |
231 | ```{code-cell} python3
232 | α_d = np.array([[2]])
233 | l_λ = np.hstack((α_d, λ_1))
234 | pref2 = Preferences(β, l_λ, π_h, δ_h, θ_h)
235 | econ2 = DLE(info1, tech1, pref2)
236 |
237 | α_d = np.array([[0.1]])
238 |
239 | k = 7
240 | λ_1 = np.full((1, k), ε_1)
241 | l_λ = np.hstack((α_d, λ_1))
242 | d1 = np.vstack((δ_n, np.zeros((k - 1, 1))))
243 | d2 = np.hstack((d1, np.eye(k)))
244 | δ_h = np.vstack((d2, np.zeros((1, k+1))))
245 | θ_h = np.vstack((np.zeros((k, 1)),
246 | np.ones((1, 1))))
247 |
248 | Pref3 = Preferences(β, l_λ, π_h, δ_h, θ_h)
249 | econ3 = DLE(info1, tech1, Pref3)
250 |
251 | k = 10
252 | λ_1 = np.full((1, k), ε_1)
253 | l_λ = np.hstack((α_d, λ_1))
254 | d1 = np.vstack((δ_n, np.zeros((k - 1, 1))))
255 | d2 = np.hstack((d1, np.eye(k)))
256 | δ_h = np.vstack((d2, np.zeros((1, k + 1))))
257 | θ_h = np.vstack((np.zeros((k, 1)),
258 | np.ones((1, 1))))
259 |
260 | pref4 = Preferences(β, l_λ, π_h, δ_h, θ_h)
261 | econ4 = DLE(info1, tech1, pref4)
262 |
263 | shock_demand = np.array([[0], [1]])
264 |
265 | econ1.irf(ts_length=25, shock=shock_demand)
266 | econ2.irf(ts_length=25, shock=shock_demand)
267 | econ3.irf(ts_length=25, shock=shock_demand)
268 | econ4.irf(ts_length=25, shock=shock_demand)
269 | ```
270 |
271 | The first figure plots the impulse response of $n_t$ (on the left)
272 | and $N_t$ (on the right) to a positive demand shock, for
273 | $\alpha_d = 0.1$ and $\alpha_d = 2$.
274 |
275 | When $\alpha_d = 2$, the number of new students $n_t$ rises
276 | initially, but the response then turns negative.
277 |
278 | A positive demand shock raises wages, drawing new students into the
279 | profession.
280 |
281 | However, these new students raise $N_t$.
282 |
283 | The higher is $\alpha_d$, the larger the effect of this rise in
284 | $N_t$ on wages.
285 |
286 | This counteracts the demand shock's positive effect on wages, reducing
287 | the number of new students in subsequent periods.
288 |
289 | Consequently, when $\alpha_d$ is lower, the effect of a demand
290 | shock on $N_t$ is larger
291 |
292 | ```{code-cell} python3
293 | fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 4))
294 | ax1.plot(econ1.c_irf,label=r'$\alpha_d = 0.1$')
295 | ax1.plot(econ2.c_irf,label=r'$\alpha_d = 2$')
296 | ax1.legend()
297 | ax1.set_title('Response of $n_t$ to a demand shock')
298 |
299 | ax2.plot(econ1.h_irf[:, 0], label=r'$\alpha_d = 0.1$')
300 | ax2.plot(econ2.h_irf[:, 0], label=r'$\alpha_d = 24$')
301 | ax2.legend()
302 | ax2.set_title('Response of $N_t$ to a demand shock')
303 | plt.show()
304 | ```
305 |
306 | The next figure plots the impulse response of $n_t$ (on the left)
307 | and $N_t$ (on the right) to a positive demand shock, for
308 | $k=4$, $k=7$ and $k=10$ (with $\alpha_d = 0.1$)
309 |
310 | ```{code-cell} python3
311 | fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 4))
312 | ax1.plot(econ1.c_irf, label='$k=4$')
313 | ax1.plot(econ3.c_irf, label='$k=7$')
314 | ax1.plot(econ4.c_irf, label='$k=10$')
315 | ax1.legend()
316 | ax1.set_title('Response of $n_t$ to a demand shock')
317 |
318 | ax2.plot(econ1.h_irf[:,0], label='$k=4$')
319 | ax2.plot(econ3.h_irf[:,0], label='$k=7$')
320 | ax2.plot(econ4.h_irf[:,0], label='$k=10$')
321 | ax2.legend()
322 | ax2.set_title('Response of $N_t$ to a demand shock')
323 | plt.show()
324 | ```
325 |
326 | Both panels in the above figure show that raising $k$ lowers the effect of
327 | a positive demand shock on entry into the engineering profession.
328 |
329 | Increasing the number of periods of schooling lowers
330 | the number of new students in response to a demand shock.
331 |
332 | This occurs because with longer required schooling, new students ultimately benefit less from the impact of that shock on wages.
333 |
--------------------------------------------------------------------------------
/lectures/_static/lecture_specific/amss2/recursive_allocation.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | from scipy.optimize import fmin_slsqp
3 | from scipy.optimize import root
4 | from quantecon import MarkovChain
5 |
6 |
7 | class RecursiveAllocationAMSS:
8 |
9 | def __init__(self, model, μgrid, tol_diff=1e-7, tol=1e-7):
10 |
11 | self.β, self.π, self.G = model.β, model.π, model.G
12 | self.mc, self.S = MarkovChain(self.π), len(model.π) # Number of states
13 | self.Θ, self.model, self.μgrid = model.Θ, model, μgrid
14 | self.tol_diff, self.tol = tol_diff, tol
15 |
16 | # Find the first best allocation
17 | self.solve_time1_bellman()
18 | self.T.time_0 = True # Bellman equation now solves time 0 problem
19 |
20 | def solve_time1_bellman(self):
21 | '''
22 | Solve the time 1 Bellman equation for calibration model and
23 | initial grid μgrid0
24 | '''
25 | model, μgrid0 = self.model, self.μgrid
26 | π = model.π
27 | S = len(model.π)
28 |
29 | # First get initial fit from Lucas Stokey solution.
30 | # Need to change things to be ex ante
31 | pp = SequentialAllocation(model)
32 | interp = interpolator_factory(2, None)
33 |
34 | def incomplete_allocation(μ_, s_):
35 | c, n, x, V = pp.time1_value(μ_)
36 | return c, n, π[s_] @ x, π[s_] @ V
37 | cf, nf, xgrid, Vf, xprimef = [], [], [], [], []
38 | for s_ in range(S):
39 | c, n, x, V = zip(*map(lambda μ: incomplete_allocation(μ, s_), μgrid0))
40 | c, n = np.vstack(c).T, np.vstack(n).T
41 | x, V = np.hstack(x), np.hstack(V)
42 | xprimes = np.vstack([x] * S)
43 | cf.append(interp(x, c))
44 | nf.append(interp(x, n))
45 | Vf.append(interp(x, V))
46 | xgrid.append(x)
47 | xprimef.append(interp(x, xprimes))
48 | cf, nf, xprimef = fun_vstack(cf), fun_vstack(nf), fun_vstack(xprimef)
49 | Vf = fun_hstack(Vf)
50 | policies = [cf, nf, xprimef]
51 |
52 | # Create xgrid
53 | x = np.vstack(xgrid).T
54 | xbar = [x.min(0).max(), x.max(0).min()]
55 | xgrid = np.linspace(xbar[0], xbar[1], len(μgrid0))
56 | self.xgrid = xgrid
57 |
58 | # Now iterate on Bellman equation
59 | T = BellmanEquation(model, xgrid, policies, tol=self.tol)
60 | diff = 1
61 | while diff > self.tol_diff:
62 | PF = T(Vf)
63 |
64 | Vfnew, policies = self.fit_policy_function(PF)
65 | diff = np.abs((Vf(xgrid) - Vfnew(xgrid)) / Vf(xgrid)).max()
66 |
67 | print(diff)
68 | Vf = Vfnew
69 |
70 | # Store value function policies and Bellman Equations
71 | self.Vf = Vf
72 | self.policies = policies
73 | self.T = T
74 |
75 | def fit_policy_function(self, PF):
76 | '''
77 | Fits the policy functions
78 | '''
79 | S, xgrid = len(self.π), self.xgrid
80 | interp = interpolator_factory(3, 0)
81 | cf, nf, xprimef, Tf, Vf = [], [], [], [], []
82 | for s_ in range(S):
83 | PFvec = np.vstack([PF(x, s_) for x in self.xgrid]).T
84 | Vf.append(interp(xgrid, PFvec[0, :]))
85 | cf.append(interp(xgrid, PFvec[1:1 + S]))
86 | nf.append(interp(xgrid, PFvec[1 + S:1 + 2 * S]))
87 | xprimef.append(interp(xgrid, PFvec[1 + 2 * S:1 + 3 * S]))
88 | Tf.append(interp(xgrid, PFvec[1 + 3 * S:]))
89 | policies = fun_vstack(cf), fun_vstack(
90 | nf), fun_vstack(xprimef), fun_vstack(Tf)
91 | Vf = fun_hstack(Vf)
92 | return Vf, policies
93 |
94 | def Τ(self, c, n):
95 | '''
96 | Computes Τ given c and n
97 | '''
98 | model = self.model
99 | Uc, Un = model.Uc(c, n), model.Un(c, n)
100 |
101 | return 1 + Un / (self.Θ * Uc)
102 |
103 | def time0_allocation(self, B_, s0):
104 | '''
105 | Finds the optimal allocation given initial government debt B_ and
106 | state s_0
107 | '''
108 | PF = self.T(self.Vf)
109 | z0 = PF(B_, s0)
110 | c0, n0, xprime0, T0 = z0[1:]
111 | return c0, n0, xprime0, T0
112 |
113 | def simulate(self, B_, s_0, T, sHist=None):
114 | '''
115 | Simulates planners policies for T periods
116 | '''
117 | model, π = self.model, self.π
118 | Uc = model.Uc
119 | cf, nf, xprimef, Tf = self.policies
120 |
121 | if sHist is None:
122 | sHist = simulate_markov(π, s_0, T)
123 |
124 | cHist, nHist, Bhist, xHist, ΤHist, THist, μHist = np.zeros((7, T))
125 | # Time 0
126 | cHist[0], nHist[0], xHist[0], THist[0] = self.time0_allocation(B_, s_0)
127 | ΤHist[0] = self.Τ(cHist[0], nHist[0])[s_0]
128 | Bhist[0] = B_
129 | μHist[0] = self.Vf[s_0](xHist[0])
130 |
131 | # Time 1 onward
132 | for t in range(1, T):
133 | s_, x, s = sHist[t - 1], xHist[t - 1], sHist[t]
134 | c, n, xprime, T = cf[s_, :](x), nf[s_, :](
135 | x), xprimef[s_, :](x), Tf[s_, :](x)
136 |
137 | Τ = self.Τ(c, n)[s]
138 | u_c = Uc(c, n)
139 | Eu_c = π[s_, :] @ u_c
140 |
141 | μHist[t] = self.Vf[s](xprime[s])
142 |
143 | cHist[t], nHist[t], Bhist[t], ΤHist[t] = c[s], n[s], x / Eu_c, Τ
144 | xHist[t], THist[t] = xprime[s], T[s]
145 | return [cHist, nHist, Bhist, ΤHist, THist, μHist, sHist, xHist]
146 |
147 |
148 | class BellmanEquation:
149 | '''
150 | Bellman equation for the continuation of the Lucas-Stokey Problem
151 | '''
152 |
153 | def __init__(self, model, xgrid, policies0, tol, maxiter=1000):
154 |
155 | self.β, self.π, self.G = model.β, model.π, model.G
156 | self.S = len(model.π) # Number of states
157 | self.Θ, self.model, self.tol = model.Θ, model, tol
158 | self.maxiter = maxiter
159 |
160 | self.xbar = [min(xgrid), max(xgrid)]
161 | self.time_0 = False
162 |
163 | self.z0 = {}
164 | cf, nf, xprimef = policies0
165 |
166 | for s_ in range(self.S):
167 | for x in xgrid:
168 | self.z0[x, s_] = np.hstack([cf[s_, :](x),
169 | nf[s_, :](x),
170 | xprimef[s_, :](x),
171 | np.zeros(self.S)])
172 |
173 | self.find_first_best()
174 |
175 | def find_first_best(self):
176 | '''
177 | Find the first best allocation
178 | '''
179 | model = self.model
180 | S, Θ, Uc, Un, G = self.S, self.Θ, model.Uc, model.Un, self.G
181 |
182 | def res(z):
183 | c = z[:S]
184 | n = z[S:]
185 | return np.hstack([Θ * Uc(c, n) + Un(c, n), Θ * n - c - G])
186 |
187 | res = root(res, np.full(2 * S, 0.5))
188 | if not res.success:
189 | raise Exception('Could not find first best')
190 |
191 | self.cFB = res.x[:S]
192 | self.nFB = res.x[S:]
193 | IFB = Uc(self.cFB, self.nFB) * self.cFB + \
194 | Un(self.cFB, self.nFB) * self.nFB
195 |
196 | self.xFB = np.linalg.solve(np.eye(S) - self.β * self.π, IFB)
197 |
198 | self.zFB = {}
199 | for s in range(S):
200 | self.zFB[s] = np.hstack(
201 | [self.cFB[s], self.nFB[s], self.π[s] @ self.xFB, 0.])
202 |
203 | def __call__(self, Vf):
204 | '''
205 | Given continuation value function next period return value function this
206 | period return T(V) and optimal policies
207 | '''
208 | if not self.time_0:
209 | def PF(x, s): return self.get_policies_time1(x, s, Vf)
210 | else:
211 | def PF(B_, s0): return self.get_policies_time0(B_, s0, Vf)
212 | return PF
213 |
214 | def get_policies_time1(self, x, s_, Vf):
215 | '''
216 | Finds the optimal policies
217 | '''
218 | model, β, Θ, G, S, π = self.model, self.β, self.Θ, self.G, self.S, self.π
219 | U, Uc, Un = model.U, model.Uc, model.Un
220 |
221 | def objf(z):
222 | c, n, xprime = z[:S], z[S:2 * S], z[2 * S:3 * S]
223 |
224 | Vprime = np.empty(S)
225 | for s in range(S):
226 | Vprime[s] = Vf[s](xprime[s])
227 |
228 | return -π[s_] @ (U(c, n) + β * Vprime)
229 |
230 | def objf_prime(x):
231 |
232 | epsilon = 1e-7
233 | x0 = np.asarray(x, dtype=float)
234 | f0 = np.atleast_1d(objf(x0))
235 | jac = np.zeros([len(x0), len(f0)])
236 | dx = np.zeros(len(x0))
237 | for i in range(len(x0)):
238 | dx[i] = epsilon
239 | jac[i] = (objf(x0+dx) - f0)/epsilon
240 | dx[i] = 0.0
241 |
242 | return jac.transpose()
243 |
244 | def cons(z):
245 | c, n, xprime, T = z[:S], z[S:2 * S], z[2 * S:3 * S], z[3 * S:]
246 | u_c = Uc(c, n)
247 | Eu_c = π[s_] @ u_c
248 | return np.hstack([
249 | x * u_c / Eu_c - u_c * (c - T) - Un(c, n) * n - β * xprime,
250 | Θ * n - c - G])
251 |
252 | if model.transfers:
253 | bounds = [(0., 100)] * S + [(0., 100)] * S + \
254 | [self.xbar] * S + [(0., 100.)] * S
255 | else:
256 | bounds = [(0., 100)] * S + [(0., 100)] * S + \
257 | [self.xbar] * S + [(0., 0.)] * S
258 | out, fx, _, imode, smode = fmin_slsqp(objf, self.z0[x, s_],
259 | f_eqcons=cons, bounds=bounds,
260 | fprime=objf_prime, full_output=True,
261 | iprint=0, acc=self.tol, iter=self.maxiter)
262 |
263 | if imode > 0:
264 | raise Exception(smode)
265 |
266 | self.z0[x, s_] = out
267 | return np.hstack([-fx, out])
268 |
269 | def get_policies_time0(self, B_, s0, Vf):
270 | '''
271 | Finds the optimal policies
272 | '''
273 | model, β, Θ, G = self.model, self.β, self.Θ, self.G
274 | U, Uc, Un = model.U, model.Uc, model.Un
275 |
276 | def objf(z):
277 | c, n, xprime = z[:-1]
278 |
279 | return -(U(c, n) + β * Vf[s0](xprime))
280 |
281 | def cons(z):
282 | c, n, xprime, T = z
283 | return np.hstack([
284 | -Uc(c, n) * (c - B_ - T) - Un(c, n) * n - β * xprime,
285 | (Θ * n - c - G)[s0]])
286 |
287 | if model.transfers:
288 | bounds = [(0., 100), (0., 100), self.xbar, (0., 100.)]
289 | else:
290 | bounds = [(0., 100), (0., 100), self.xbar, (0., 0.)]
291 | out, fx, _, imode, smode = fmin_slsqp(objf, self.zFB[s0], f_eqcons=cons,
292 | bounds=bounds, full_output=True,
293 | iprint=0)
294 |
295 | if imode > 0:
296 | raise Exception(smode)
297 |
298 | return np.hstack([-fx, out])
299 |
--------------------------------------------------------------------------------
/lectures/_static/lecture_specific/lu_tricks/control_and_filter.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import scipy.stats as spst
3 | import scipy.linalg as la
4 |
5 | class LQFilter:
6 |
7 | def __init__(self, d, h, y_m, r=None, h_eps=None, β=None):
8 | """
9 |
10 | Parameters
11 | ----------
12 | d : list or numpy.array (1-D or a 2-D column vector)
13 | The order of the coefficients: [d_0, d_1, ..., d_m]
14 | h : scalar
15 | Parameter of the objective function (corresponding to the
16 | quadratic term)
17 | y_m : list or numpy.array (1-D or a 2-D column vector)
18 | Initial conditions for y
19 | r : list or numpy.array (1-D or a 2-D column vector)
20 | The order of the coefficients: [r_0, r_1, ..., r_k]
21 | (optional, if not defined -> deterministic problem)
22 | β : scalar
23 | Discount factor (optional, default value is one)
24 | """
25 |
26 | self.h = h
27 | self.d = np.asarray(d)
28 | self.m = self.d.shape[0] - 1
29 |
30 | self.y_m = np.asarray(y_m)
31 |
32 | if self.m == self.y_m.shape[0]:
33 | self.y_m = self.y_m.reshape(self.m, 1)
34 | else:
35 | raise ValueError("y_m must be of length m = {self.m:d}")
36 |
37 | #---------------------------------------------
38 | # Define the coefficients of ϕ upfront
39 | #---------------------------------------------
40 | ϕ = np.zeros(2 * self.m + 1)
41 | for i in range(- self.m, self.m + 1):
42 | ϕ[self.m - i] = np.sum(np.diag(self.d.reshape(self.m + 1, 1) \
43 | @ self.d.reshape(1, self.m + 1),
44 | k=-i
45 | )
46 | )
47 | ϕ[self.m] = ϕ[self.m] + self.h
48 | self.ϕ = ϕ
49 |
50 | #-----------------------------------------------------
51 | # If r is given calculate the vector ϕ_r
52 | #-----------------------------------------------------
53 | if r is None:
54 | pass
55 | else:
56 | self.r = np.asarray(r)
57 | self.k = self.r.shape[0] - 1
58 | ϕ_r = np.zeros(2 * self.k + 1)
59 | for i in range(- self.k, self.k + 1):
60 | ϕ_r[self.k - i] = np.sum(np.diag(self.r.reshape(self.k + 1, 1) \
61 | @ self.r.reshape(1, self.k + 1),
62 | k=-i
63 | )
64 | )
65 | if h_eps is None:
66 | self.ϕ_r = ϕ_r
67 | else:
68 | ϕ_r[self.k] = ϕ_r[self.k] + h_eps
69 | self.ϕ_r = ϕ_r
70 |
71 | #-----------------------------------------------------
72 | # If β is given, define the transformed variables
73 | #-----------------------------------------------------
74 | if β is None:
75 | self.β = 1
76 | else:
77 | self.β = β
78 | self.d = self.β**(np.arange(self.m + 1)/2) * self.d
79 | self.y_m = self.y_m * (self.β**(- np.arange(1, self.m + 1)/2)) \
80 | .reshape(self.m, 1)
81 |
82 | def construct_W_and_Wm(self, N):
83 | """
84 | This constructs the matrices W and W_m for a given number of periods N
85 | """
86 |
87 | m = self.m
88 | d = self.d
89 |
90 | W = np.zeros((N + 1, N + 1))
91 | W_m = np.zeros((N + 1, m))
92 |
93 | #---------------------------------------
94 | # Terminal conditions
95 | #---------------------------------------
96 |
97 | D_m1 = np.zeros((m + 1, m + 1))
98 | M = np.zeros((m + 1, m))
99 |
100 | # (1) Constuct the D_{m+1} matrix using the formula
101 |
102 | for j in range(m + 1):
103 | for k in range(j, m + 1):
104 | D_m1[j, k] = d[:j + 1] @ d[k - j: k + 1]
105 |
106 | # Make the matrix symmetric
107 | D_m1 = D_m1 + D_m1.T - np.diag(np.diag(D_m1))
108 |
109 | # (2) Construct the M matrix using the entries of D_m1
110 |
111 | for j in range(m):
112 | for i in range(j + 1, m + 1):
113 | M[i, j] = D_m1[i - j - 1, m]
114 |
115 | #----------------------------------------------
116 | # Euler equations for t = 0, 1, ..., N-(m+1)
117 | #----------------------------------------------
118 | ϕ = self.ϕ
119 |
120 | W[:(m + 1), :(m + 1)] = D_m1 + self.h * np.eye(m + 1)
121 | W[:(m + 1), (m + 1):(2 * m + 1)] = M
122 |
123 | for i, row in enumerate(np.arange(m + 1, N + 1 - m)):
124 | W[row, (i + 1):(2 * m + 2 + i)] = ϕ
125 |
126 | for i in range(1, m + 1):
127 | W[N - m + i, -(2 * m + 1 - i):] = ϕ[:-i]
128 |
129 | for i in range(m):
130 | W_m[N - i, :(m - i)] = ϕ[(m + 1 + i):]
131 |
132 | return W, W_m
133 |
134 | def roots_of_characteristic(self):
135 | """
136 | This function calculates z_0 and the 2m roots of the characteristic
137 | equation associated with the Euler equation (1.7)
138 |
139 | Note:
140 | ------
141 | numpy.poly1d(roots, True) defines a polynomial using its roots that can
142 | be evaluated at any point. If x_1, x_2, ... , x_m are the roots then
143 | p(x) = (x - x_1)(x - x_2)...(x - x_m)
144 | """
145 | m = self.m
146 | ϕ = self.ϕ
147 |
148 | # Calculate the roots of the 2m-polynomial
149 | roots = np.roots(ϕ)
150 | # Sort the roots according to their length (in descending order)
151 | roots_sorted = roots[np.argsort(abs(roots))[::-1]]
152 |
153 | z_0 = ϕ.sum() / np.poly1d(roots, True)(1)
154 | z_1_to_m = roots_sorted[:m] # We need only those outside the unit circle
155 |
156 | λ = 1 / z_1_to_m
157 |
158 | return z_1_to_m, z_0, λ
159 |
160 | def coeffs_of_c(self):
161 | '''
162 | This function computes the coefficients {c_j, j = 0, 1, ..., m} for
163 | c(z) = sum_{j = 0}^{m} c_j z^j
164 |
165 | Based on the expression (1.9). The order is
166 | c_coeffs = [c_0, c_1, ..., c_{m-1}, c_m]
167 | '''
168 | z_1_to_m, z_0 = self.roots_of_characteristic()[:2]
169 |
170 | c_0 = (z_0 * np.prod(z_1_to_m).real * (- 1)**self.m)**(.5)
171 | c_coeffs = np.poly1d(z_1_to_m, True).c * z_0 / c_0
172 |
173 | return c_coeffs[::-1]
174 |
175 | def solution(self):
176 | """
177 | This function calculates {λ_j, j=1,...,m} and {A_j, j=1,...,m}
178 | of the expression (1.15)
179 | """
180 | λ = self.roots_of_characteristic()[2]
181 | c_0 = self.coeffs_of_c()[-1]
182 |
183 | A = np.zeros(self.m, dtype=complex)
184 | for j in range(self.m):
185 | denom = 1 - λ/λ[j]
186 | A[j] = c_0**(-2) / np.prod(denom[np.arange(self.m) != j])
187 |
188 | return λ, A
189 |
190 | def construct_V(self, N):
191 | '''
192 | This function constructs the covariance matrix for x^N (see section 6)
193 | for a given period N
194 | '''
195 | V = np.zeros((N, N))
196 | ϕ_r = self.ϕ_r
197 |
198 | for i in range(N):
199 | for j in range(N):
200 | if abs(i-j) <= self.k:
201 | V[i, j] = ϕ_r[self.k + abs(i-j)]
202 |
203 | return V
204 |
205 | def simulate_a(self, N):
206 | """
207 | Assuming that the u's are normal, this method draws a random path
208 | for x^N
209 | """
210 | V = self.construct_V(N + 1)
211 | d = spst.multivariate_normal(np.zeros(N + 1), V)
212 |
213 | return d.rvs()
214 |
215 | def predict(self, a_hist, t):
216 | """
217 | This function implements the prediction formula discussed in section 6 (1.59)
218 | It takes a realization for a^N, and the period in which the prediction is
219 | formed
220 |
221 | Output: E[abar | a_t, a_{t-1}, ..., a_1, a_0]
222 | """
223 |
224 | N = np.asarray(a_hist).shape[0] - 1
225 | a_hist = np.asarray(a_hist).reshape(N + 1, 1)
226 | V = self.construct_V(N + 1)
227 |
228 | aux_matrix = np.zeros((N + 1, N + 1))
229 | aux_matrix[:(t + 1), :(t + 1)] = np.eye(t + 1)
230 | L = la.cholesky(V).T
231 | Ea_hist = la.inv(L) @ aux_matrix @ L @ a_hist
232 |
233 | return Ea_hist
234 |
235 | def optimal_y(self, a_hist, t=None):
236 | """
237 | - if t is NOT given it takes a_hist (list or numpy.array) as a
238 | deterministic a_t
239 | - if t is given, it solves the combined control prediction problem
240 | (section 7)(by default, t == None -> deterministic)
241 |
242 | for a given sequence of a_t (either deterministic or a particular
243 | realization), it calculates the optimal y_t sequence using the method
244 | of the lecture
245 |
246 | Note:
247 | ------
248 | scipy.linalg.lu normalizes L, U so that L has unit diagonal elements
249 | To make things consistent with the lecture, we need an auxiliary
250 | diagonal matrix D which renormalizes L and U
251 | """
252 |
253 | N = np.asarray(a_hist).shape[0] - 1
254 | W, W_m = self.construct_W_and_Wm(N)
255 |
256 | L, U = la.lu(W, permute_l=True)
257 | D = np.diag(1 / np.diag(U))
258 | U = D @ U
259 | L = L @ np.diag(1 / np.diag(D))
260 |
261 | J = np.fliplr(np.eye(N + 1))
262 |
263 | if t is None: # If the problem is deterministic
264 |
265 | a_hist = J @ np.asarray(a_hist).reshape(N + 1, 1)
266 |
267 | #--------------------------------------------
268 | # Transform the 'a' sequence if β is given
269 | #--------------------------------------------
270 | if self.β != 1:
271 | a_hist = a_hist * (self.β**(np.arange(N + 1) / 2))[::-1] \
272 | .reshape(N + 1, 1)
273 |
274 | a_bar = a_hist - W_m @ self.y_m # a_bar from the lecture
275 | Uy = np.linalg.solve(L, a_bar) # U @ y_bar = L^{-1}
276 | y_bar = np.linalg.solve(U, Uy) # y_bar = U^{-1}L^{-1}
277 |
278 | # Reverse the order of y_bar with the matrix J
279 | J = np.fliplr(np.eye(N + self.m + 1))
280 | # y_hist : concatenated y_m and y_bar
281 | y_hist = J @ np.vstack([y_bar, self.y_m])
282 |
283 | #--------------------------------------------
284 | # Transform the optimal sequence back if β is given
285 | #--------------------------------------------
286 | if self.β != 1:
287 | y_hist = y_hist * (self.β**(- np.arange(-self.m, N + 1)/2)) \
288 | .reshape(N + 1 + self.m, 1)
289 |
290 | return y_hist, L, U, y_bar
291 |
292 | else: # If the problem is stochastic and we look at it
293 |
294 | Ea_hist = self.predict(a_hist, t).reshape(N + 1, 1)
295 | Ea_hist = J @ Ea_hist
296 |
297 | a_bar = Ea_hist - W_m @ self.y_m # a_bar from the lecture
298 | Uy = np.linalg.solve(L, a_bar) # U @ y_bar = L^{-1}
299 | y_bar = np.linalg.solve(U, Uy) # y_bar = U^{-1}L^{-1}
300 |
301 | # Reverse the order of y_bar with the matrix J
302 | J = np.fliplr(np.eye(N + self.m + 1))
303 | # y_hist : concatenated y_m and y_bar
304 | y_hist = J @ np.vstack([y_bar, self.y_m])
305 |
306 | return y_hist, L, U, y_bar
307 |
--------------------------------------------------------------------------------
/lectures/hs_invertibility_example.md:
--------------------------------------------------------------------------------
1 | ---
2 | jupytext:
3 | text_representation:
4 | extension: .md
5 | format_name: myst
6 | kernelspec:
7 | display_name: Python 3
8 | language: python
9 | name: python3
10 | ---
11 |
12 | (hs_invertibility_example)=
13 | ```{raw} html
14 |
19 | ```
20 |
21 | ```{index} single: python
22 | ```
23 |
24 | # Shock Non Invertibility
25 |
26 | ## Overview
27 |
28 | This is another member of a suite of lectures that use the quantecon DLE class to instantiate models within the
29 | {cite}`HS2013` class of models described in {doc}`Recursive Models of Dynamic Linear Economies `.
30 |
31 | In addition to what's in Anaconda, this lecture uses the quantecon library.
32 |
33 | ```{code-cell} ipython
34 | ---
35 | tags: [hide-output]
36 | ---
37 | !pip install --upgrade quantecon
38 | ```
39 |
40 | We'll make these imports:
41 |
42 | ```{code-cell} ipython
43 | import numpy as np
44 | import quantecon as qe
45 | import matplotlib.pyplot as plt
46 | from quantecon import DLE
47 | from math import sqrt
48 | ```
49 |
50 | This lecture describes an early contribution to what is now often called
51 | a **news and noise** issue.
52 |
53 | In particular, it analyzes a **shock-invertibility** issue that is
54 | endemic within a class of permanent income models.
55 |
56 | Technically, the invertibility problem indicates a situation in which
57 | histories of the shocks in an econometrician's autoregressive or Wold
58 | moving average representation span a smaller information space than do
59 | the shocks that are seen by the agents inside the econometrician's model.
60 |
61 | An econometrician who is unaware of the
62 | problem would misinterpret shocks and likely responses to them.
63 |
64 | A shock-invertibility that is technically close to the one studied here is discussed by
65 | Eric Leeper, Todd Walker, and Susan Yang {cite}`Leeper_Walker_Yang` in their analysis of **fiscal foresight**.
66 |
67 | A distinct shock-invertibility issue is present in the special LQ consumption smoothing model
68 | in this quantecon lecture {doc}`cons_news`.
69 |
70 | ## Model
71 |
72 | We consider the following modification of Robert Hall's (1978) model {cite}`Hall1978` in which the endowment process is the sum of two orthogonal autoregressive processes:
73 |
74 | **Preferences**
75 |
76 | $$
77 | -\frac{1}{2}\mathbb{E}\sum_{t=0}^\infty \beta^t[(c_t - b_t)^2 + l_t^2]|J_0
78 | $$
79 |
80 | $$
81 | s_t = c_t
82 | $$
83 |
84 | $$
85 | b_t = U_bz_t
86 | $$
87 |
88 | **Technology**
89 |
90 | $$
91 | c_t + i_t = \gamma k_{t-1} + d_t
92 | $$
93 |
94 | $$
95 | k_t = \delta_k k_{t-1} + i_t
96 | $$
97 |
98 | $$
99 | g_t = \phi_1 i_t \, , \phi_1 > 0
100 | $$
101 |
102 | $$
103 | g_t \cdot g_t = l_t^2
104 | $$
105 |
106 | **Information**
107 |
108 | $$
109 | z_{t+1} =
110 | \left[ {\begin{array}{cccccc}
111 | 1 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0.9 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 1 & 0 & 0 & 0 \\ 0 & 0 & 0 & 1 & 0 & 0 \\ 0 & 0 & 0 & 0 & 1 & 0
112 | \end{array} }
113 | \right]
114 | z_t +
115 | \left[ {\begin{array}{cc}
116 | 0 & 0 \\ 1 & 0 \\ 0 & 4 \\ 0 & 0 \\ 0 & 0 \\ 0 & 0
117 | \end{array} }
118 | \right]
119 | w_{t+1}
120 | $$
121 |
122 | $$
123 | U_b =
124 | \left[ {\begin{array}{cccccc}
125 | 30 & 0 & 0 & 0 & 0 & 0
126 | \end{array} }
127 | \right]
128 | $$
129 |
130 | $$
131 | U_d =
132 | \left[ {\begin{array}{cccccc}
133 | 5 & 1 & 1 & 0.8 & 0.6 & 0.4 \\ 0 & 0 & 0 & 0 & 0 & 0
134 | \end{array} }
135 | \right]
136 | $$
137 |
138 | The preference shock is constant at 30, while the endowment process is
139 | the sum of a constant and two orthogonal processes.
140 |
141 | Specifically:
142 |
143 | $$
144 | d_t = 5 + d_{1t} + d_{2t}
145 | $$
146 |
147 | $$
148 | d_{1t} = 0.9d_{1t-1} + w_{1t}
149 | $$
150 |
151 | $$
152 | d_{2t} = 4w_{2t} + 0.8(4w_{2t-1})+ 0.6(4w_{2t-2})+ 0.4(4w_{2t-3})
153 | $$
154 |
155 | $d_{1t}$ is a first-order AR process, while $d_{2t}$ is a
156 | third-order pure moving average process.
157 |
158 | ```{code-cell} python3
159 | γ_1 = 0.05
160 | γ = np.array([[γ_1], [0]])
161 | ϕ_c = np.array([[1], [0]])
162 | ϕ_g = np.array([[0], [1]])
163 | ϕ_1 = 0.00001
164 | ϕ_i = np.array([[1], [-ϕ_1]])
165 | δ_k = np.array([[1]])
166 | θ_k = np.array([[1]])
167 | β = np.array([[1 / 1.05]])
168 | l_λ = np.array([[0]])
169 | π_h = np.array([[1]])
170 | δ_h = np.array([[.9]])
171 | θ_h = np.array([[1]]) - δ_h
172 | ud = np.array([[5, 1, 1, 0.8, 0.6, 0.4],
173 | [0, 0, 0, 0, 0, 0]])
174 | a22 = np.zeros((6, 6))
175 | # Chase's great trick
176 | a22[[0, 1, 3, 4, 5], [0, 1, 2, 3, 4]] = np.array([1.0, 0.9, 1.0, 1.0, 1.0])
177 | c2 = np.zeros((6, 2))
178 | c2[[1, 2], [0, 1]] = np.array([1.0, 4.0])
179 | ub = np.array([[30, 0, 0, 0, 0, 0]])
180 | x0 = np.array([[5], [150], [1], [0], [0], [0], [0], [0]])
181 |
182 | info1 = (a22, c2, ub, ud)
183 | tech1 = (ϕ_c, ϕ_g, ϕ_i, γ, δ_k, θ_k)
184 | pref1 = (β, l_λ, π_h, δ_h, θ_h)
185 |
186 | econ1 = DLE(info1, tech1, pref1)
187 | ```
188 |
189 | We define the household's net of interest deficit as $c_t - d_t$.
190 |
191 | Hall's model imposes "expected present-value budget balance" in the
192 | sense that
193 |
194 | $$
195 | \mathbb{E}\sum_{j=0}^\infty \beta^j (c_{t+j} - d_{t+j})|J_t = \beta^{-1}k_{t-1} \, \forall t
196 | $$
197 |
198 | Define a moving average representation of
199 | $(c_t, c_t - d_t)$ in terms of the $w_t$s to be:
200 |
201 | $$
202 | \left[ {\begin{array}{c}
203 | c_t \\ c_t - d_t
204 | \end{array} }
205 | \right] = \left[ {\begin{array}{c}
206 | \sigma_1(L) \\ \sigma_2(L)
207 | \end{array} }
208 | \right] w_t
209 | $$
210 |
211 | Hall's model imposes the restriction
212 | $\sigma_2(\beta) = [0\,\,\,0]$.
213 |
214 | - The consumer who lives inside this model observes histories of both components of the
215 | endowment process $d_{1t}$ and $d_{2t}$.
216 |
217 | - The econometrician has data on the history of the pair
218 | $[c_t,d_t]$, but not directly on the history of $w_t$'s.
219 |
220 | - The econometrician obtains a Wold representation for the process
221 | $[c_t,c_t-d_t]$:
222 |
223 | $$
224 | \left[ {\begin{array}{c}
225 | c_t \\ c_t - d_t
226 | \end{array} }
227 | \right] = \left[ {\begin{array}{c}
228 | \sigma_1^*(L) \\ \sigma_2^*(L)
229 | \end{array} }
230 | \right] u_t
231 | $$
232 |
233 | A representation with equivalent shocks would be recovered by estimating a bivariate vector autoregression for $c_t, c_t-d_t$.
234 |
235 | The Appendix of chapter 8 of {cite}`HS2013` explains why the impulse
236 | response functions in the Wold representation estimated by the
237 | econometrician do not resemble the impulse response functions that
238 | depict the response of consumption and the net-of-interest deficit to innovations $w_t$ to
239 | the consumer's information.
240 |
241 | Technically, $\sigma_2(\beta) = [0\,\,\,0]$ implies that the
242 | history of $u_t$s spans a *smaller* linear space than does the
243 | history of $w_t$s.
244 |
245 | This means that $u_t$ will typically be a distributed lag of
246 | $w_t$ that is not concentrated at zero lag:
247 |
248 | $$
249 | u_t = \sum_{j=0}^\infty \alpha_j w_{t-j}
250 | $$
251 |
252 | Thus, the econometrician's news $u_t$ typically responds
253 | belatedly to the consumer's news $w_t$.
254 |
255 | ## Code
256 |
257 | We will construct Figures from Chapter 8 Appendix E of {cite}`HS2013` to
258 | illustrate these ideas:
259 |
260 | ```{code-cell} python3
261 | # This is Fig 8.E.1 from p.188 of HS2013
262 |
263 | econ1.irf(ts_length=40, shock=None)
264 |
265 | fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 4))
266 | ax1.plot(econ1.c_irf, label='Consumption')
267 | ax1.plot(econ1.c_irf - econ1.d_irf[:,0].reshape(40,1), label='Deficit')
268 | ax1.legend()
269 | ax1.set_title('Response to $w_{1t}$')
270 |
271 | shock2 = np.array([[0], [1]])
272 | econ1.irf(ts_length=40, shock=shock2)
273 |
274 | ax2.plot(econ1.c_irf, label='Consumption')
275 | ax2.plot(econ1.c_irf - econ1.d_irf[:,0].reshape(40, 1), label='Deficit')
276 | ax2.legend()
277 | ax2.set_title('Response to $w_{2t}$')
278 | plt.show()
279 | ```
280 |
281 | The above figure displays the impulse response of consumption and the
282 | net-of-interest deficit to the innovations $w_t$ to the consumer's non-financial income
283 | or endowment process.
284 |
285 | Consumption displays the characteristic "random walk" response with
286 | respect to each innovation.
287 |
288 | Each endowment innovation leads to a
289 | temporary surplus followed by a permanent net-of-interest deficit.
290 |
291 | The temporary surplus just offsets the permanent deficit in terms of
292 | expected present value.
293 |
294 | ```{code-cell} python3
295 | G_HS = np.vstack([econ1.Sc, econ1.Sc-econ1.Sd[0, :].reshape(1, 8)])
296 | H_HS = 1e-8 * np.eye(2) # Set very small so there is no measurement error
297 | lss_hs = qe.LinearStateSpace(econ1.A0, econ1.C, G_HS, H_HS)
298 |
299 | hs_kal = qe.Kalman(lss_hs)
300 | w_lss = hs_kal.whitener_lss()
301 | ma_coefs = hs_kal.stationary_coefficients(50, 'ma')
302 |
303 | # This is Fig 8.E.2 from p.189 of HS2013
304 |
305 | ma_coefs = ma_coefs
306 | jj = 50
307 | y1_w1 = np.empty(jj)
308 | y2_w1 = np.empty(jj)
309 | y1_w2 = np.empty(jj)
310 | y2_w2 = np.empty(jj)
311 |
312 | for t in range(jj):
313 | y1_w1[t] = ma_coefs[t][0, 0]
314 | y1_w2[t] = ma_coefs[t][0, 1]
315 | y2_w1[t] = ma_coefs[t][1, 0]
316 | y2_w2[t] = ma_coefs[t][1, 1]
317 |
318 | # This scales the impulse responses to match those in the book
319 | y1_w1 = sqrt(hs_kal.stationary_innovation_covar()[0, 0]) * y1_w1
320 | y2_w1 = sqrt(hs_kal.stationary_innovation_covar()[0, 0]) * y2_w1
321 | y1_w2 = sqrt(hs_kal.stationary_innovation_covar()[1, 1]) * y1_w2
322 | y2_w2 = sqrt(hs_kal.stationary_innovation_covar()[1, 1]) * y2_w2
323 |
324 | fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 4))
325 | ax1.plot(y1_w1, label='Consumption')
326 | ax1.plot(y2_w1, label='Deficit')
327 | ax1.legend()
328 | ax1.set_title('Response to $u_{1t}$')
329 |
330 | ax2.plot(y1_w2, label='Consumption')
331 | ax2.plot(y2_w2, label='Deficit')
332 | ax2.legend()
333 | ax2.set_title('Response to $u_{2t}$')
334 | plt.show()
335 | ```
336 |
337 | The above figure displays the impulse response of consumption and the
338 | deficit to the innovations in the econometrician's Wold representation
339 |
340 | - this is the object that would be recovered from a high order vector
341 | autoregression on the econometrician's observations.
342 |
343 | Consumption responds only to the first innovation
344 |
345 | - this is indicative of the Granger causality imposed on the
346 | $[c_t, c_t - d_t]$ process by Hall's model: consumption Granger
347 | causes $c_t - d_t$, with no reverse causality.
348 |
349 | ```{code-cell} python3
350 | # This is Fig 8.E.3 from p.189 of HS2013
351 |
352 | jj = 20
353 | irf_wlss = w_lss.impulse_response(jj)
354 | ycoefs = irf_wlss[1]
355 | # Pull out the shocks
356 | a1_w1 = np.empty(jj)
357 | a1_w2 = np.empty(jj)
358 | a2_w1 = np.empty(jj)
359 | a2_w2 = np.empty(jj)
360 |
361 | for t in range(jj):
362 | a1_w1[t] = ycoefs[t][0, 0]
363 | a1_w2[t] = ycoefs[t][0, 1]
364 | a2_w1[t] = ycoefs[t][1, 0]
365 | a2_w2[t] = ycoefs[t][1, 1]
366 |
367 | fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 4))
368 | ax1.plot(a1_w1, label='Consumption innov.')
369 | ax1.plot(a2_w1, label='Deficit innov.')
370 | ax1.set_title('Response to $w_{1t}$')
371 | ax1.legend()
372 | ax2.plot(a1_w2, label='Consumption innov.')
373 | ax2.plot(a2_w2, label='Deficit innov.')
374 | ax2.legend()
375 | ax2.set_title('Response to $w_{2t}$')
376 | plt.show()
377 | ```
378 |
379 | The above figure displays the impulse responses of $u_t$ to
380 | $w_t$, as depicted in:
381 |
382 | $$
383 | u_t = \sum_{j=0}^\infty \alpha_j w_{t-j}
384 | $$
385 |
386 | While the responses of the innovations to consumption are concentrated
387 | at lag zero for both components of $w_t$, the responses of the
388 | innovations to $(c_t - d_t)$ are spread over time (especially in
389 | response to $w_{1t}$).
390 |
391 | Thus, the innovations to $(c_t - d_t)$ as revealed by the vector
392 | autoregression depend on what the economic agent views as "old news".
393 |
--------------------------------------------------------------------------------
/lectures/muth_kalman.md:
--------------------------------------------------------------------------------
1 | ---
2 | jupytext:
3 | text_representation:
4 | extension: .md
5 | format_name: myst
6 | kernelspec:
7 | display_name: Python 3
8 | language: python
9 | name: python3
10 | ---
11 |
12 | (muth_kalman)=
13 | ```{raw} html
14 |
19 | ```
20 |
21 | ```{index} single: python
22 | ```
23 |
24 | # Reverse Engineering a la Muth
25 |
26 | In addition to what's in Anaconda, this lecture uses the quantecon library.
27 |
28 | ```{code-cell} ipython
29 | ---
30 | tags: [hide-output]
31 | ---
32 | !pip install --upgrade quantecon
33 | ```
34 |
35 | We'll also need the following imports:
36 |
37 | ```{code-cell} ipython
38 | import matplotlib.pyplot as plt
39 | import numpy as np
40 |
41 | from quantecon import Kalman
42 | from quantecon import LinearStateSpace
43 | np.set_printoptions(linewidth=120, precision=4, suppress=True)
44 | ```
45 |
46 | This lecture uses the Kalman filter to reformulate John F. Muth’s first
47 | paper {cite}`Muth1960` about rational expectations.
48 |
49 | Muth used *classical* prediction methods to reverse engineer a
50 | stochastic process that renders optimal Milton Friedman’s {cite}`Friedman1956` “adaptive
51 | expectations” scheme.
52 |
53 | ## Friedman (1956) and Muth (1960)
54 |
55 | Milton Friedman {cite}`Friedman1956` (1956) posited that
56 | consumer’s forecast their future disposable income with the adaptive
57 | expectations scheme
58 |
59 | ```{math}
60 | :label: expectations
61 |
62 | y_{t+i,t}^* = K \sum_{j=0}^\infty (1 - K)^j y_{t-j}
63 | ```
64 |
65 | where $K \in (0,1)$ and $y_{t+i,t}^*$ is a forecast of
66 | future $y$ over horizon $i$.
67 |
68 | Milton Friedman justified the **exponential smoothing** forecasting
69 | scheme {eq}`expectations` informally, noting that it seemed a plausible way to use
70 | past income to forecast future income.
71 |
72 | In his first paper about rational expectations, John F. Muth {cite}`Muth1960`
73 | reverse-engineered a univariate stochastic process
74 | $\{y_t\}_{t=- \infty}^\infty$ for which Milton Friedman’s adaptive
75 | expectations scheme gives linear least forecasts of $y_{t+j}$ for
76 | any horizon $i$.
77 |
78 | Muth sought a setting and a sense in which Friedman’s forecasting scheme
79 | is optimal.
80 |
81 | That is, Muth asked for what optimal forecasting **question** is Milton
82 | Friedman’s adaptive expectation scheme the **answer**.
83 |
84 | Muth (1960) used classical prediction methods based on lag-operators and
85 | $z$-transforms to find the answer to his question.
86 |
87 | Please see lectures {doc}`Classical Control with Linear Algebra ` and
88 | {doc}`Classical Filtering and Prediction with Linear Algebra ` for an introduction to the classical
89 | tools that Muth used.
90 |
91 | Rather than using those classical tools, in this lecture we apply the
92 | Kalman filter to express the heart of Muth’s analysis concisely.
93 |
94 | The lecture [First Look at Kalman Filter](https://python-intro.quantecon.org/kalman.html) describes the Kalman filter.
95 |
96 | We'll use limiting versions of the Kalman filter corresponding to what are called **stationary values** in that lecture.
97 |
98 | ## A process for which adaptive expectations are optimal
99 |
100 | Suppose that an observable $y_t$ is the sum of an unobserved
101 | random walk $x_t$ and an IID shock $\epsilon_{2,t}$:
102 |
103 | ```{math}
104 | :label: state-space
105 |
106 | \begin{aligned} x_{t+1} & = x_t + \sigma_x \epsilon_{1,t+1} \cr
107 | y_t & = x_t + \sigma_y \epsilon_{2,t} \end{aligned}
108 | ```
109 |
110 | where
111 |
112 | $$
113 | \begin{bmatrix} \epsilon_{1,t+1} \cr \epsilon_{2,t} \end{bmatrix} \sim {\mathcal N} (0, I)
114 | $$
115 |
116 | is an IID process.
117 |
118 | ```{note}
119 | A property of the state-space representation {eq}`state-space` is that in
120 | general neither $\epsilon_{1,t}$ nor $\epsilon_{2,t}$ is in
121 | the space spanned by square-summable linear combinations of
122 | $y_t, y_{t-1}, \ldots$.
123 | ```
124 |
125 | In general
126 | $\begin{bmatrix} \epsilon_{1,t} \cr \epsilon_{2t} \end{bmatrix}$
127 | has more information about future $y_{t+j}$’s than is contained
128 | in $y_t, y_{t-1}, \ldots$.
129 |
130 | We can use the asymptotic or stationary values of the Kalman gain and
131 | the one-step-ahead conditional state covariance matrix to compute a
132 | time-invariant *innovations representation*
133 |
134 | ```{math}
135 | :label: innovations
136 |
137 | \begin{aligned} \hat x_{t+1} & = \hat x_t + K a_t \cr
138 | y_t & = \hat x_t + a_t \end{aligned}
139 | ```
140 |
141 | where $\hat x_t = E [x_t | y_{t-1}, y_{t-2}, \ldots ]$ and
142 | $a_t = y_t - E[y_t |y_{t-1}, y_{t-2}, \ldots ]$.
143 |
144 | ```{note}
145 |
146 | A key property about an *innovations representation* is that
147 | $a_t$ is in the space spanned by square summable linear
148 | combinations of $y_t, y_{t-1}, \ldots$.
149 |
150 | ```
151 |
152 | For more ramifications of this property, see the lectures {doc}`Shock Non-Invertibility ` and
153 | {doc}`Recursive Models of Dynamic Linear Economies `.
154 |
155 | Later we’ll stack these state-space systems {eq}`state-space` and {eq}`innovations` to display some
156 | classic findings of Muth.
157 |
158 | But first, let’s create an instance of the state-space system {eq}`state-space` then
159 | apply the quantecon `Kalman` class, then uses it to construct the associated "innovations representation"
160 |
161 | ```{code-cell} python3
162 | # Make some parameter choices
163 | # sigx/sigy are state noise std err and measurement noise std err
164 | μ_0, σ_x, σ_y = 10, 1, 5
165 |
166 | # Create a LinearStateSpace object
167 | A, C, G, H = 1, σ_x, 1, σ_y
168 | ss = LinearStateSpace(A, C, G, H, mu_0=μ_0)
169 |
170 | # Set prior and initialize the Kalman type
171 | x_hat_0, Σ_0 = 10, 1
172 | kmuth = Kalman(ss, x_hat_0, Σ_0)
173 |
174 | # Computes stationary values which we need for the innovation
175 | # representation
176 | S1, K1 = kmuth.stationary_values()
177 |
178 | # Extract scalars from nested arrays
179 | S1, K1 = S1.item(), K1.item()
180 |
181 | # Form innovation representation state-space
182 | Ak, Ck, Gk, Hk = A, K1, G, 1
183 |
184 | ssk = LinearStateSpace(Ak, Ck, Gk, Hk, mu_0=x_hat_0)
185 | ```
186 |
187 | ## Some useful state-space math
188 |
189 | Now we want to map the time-invariant innovations representation {eq}`innovations` and
190 | the original state-space system {eq}`state-space` into a convenient form for deducing
191 | the impulse responses from the original shocks to the $x_t$ and
192 | $\hat x_t$.
193 |
194 | Putting both of these representations into a single state-space system
195 | is yet another application of the insight that “finding the state is an
196 | art”.
197 |
198 | We’ll define a state vector and appropriate state-space matrices that
199 | allow us to represent both systems in one fell swoop.
200 |
201 | Note that
202 |
203 | $$
204 | a_t = x_t + \sigma_y \epsilon_{2,t} - \hat x_t
205 | $$
206 |
207 | so that
208 |
209 | $$
210 | \begin{aligned} \hat x_{t+1} & = \hat x_t + K (x_t + \sigma_y \epsilon_{2,t} - \hat x_t) \cr
211 | & = (1-K) \hat x_t + K x_t + K \sigma_y \epsilon_{2,t} \end{aligned}
212 | $$
213 |
214 | The stacked system
215 |
216 | $$
217 | \begin{bmatrix} x_{t+1} \cr \hat x_{t+1} \cr \epsilon_{2,t+1} \end{bmatrix} =
218 | \begin{bmatrix} 1 & 0 & 0 \cr K & (1-K) & K \sigma_y \cr 0 & 0 & 0 \end{bmatrix}
219 | \begin{bmatrix} x_{t} \cr \hat x_t \cr \epsilon_{2,t} \end{bmatrix}+
220 | \begin{bmatrix} \sigma_x & 0 \cr 0 & 0 \cr 0 & 1 \end{bmatrix}
221 | \begin{bmatrix} \epsilon_{1,t+1} \cr \epsilon_{2,t+1} \end{bmatrix}
222 | $$
223 |
224 | $$
225 | \begin{bmatrix} y_t \cr a_t \end{bmatrix} = \begin{bmatrix} 1 & 0 & \sigma_y \cr
226 | 1 & -1 & \sigma_y \end{bmatrix} \begin{bmatrix} x_{t} \cr \hat x_t \cr \epsilon_{2,t} \end{bmatrix}
227 | $$
228 |
229 | is a state-space system that tells us how the shocks
230 | $\begin{bmatrix} \epsilon_{1,t+1} \cr \epsilon_{2,t+1} \end{bmatrix}$
231 | affect states $\hat x_{t+1}, x_t$, the observable $y_t$, and
232 | the innovation $a_t$.
233 |
234 | With this tool at our disposal, let’s form the composite system and
235 | simulate it
236 |
237 | ```{code-cell} python3
238 | # Create grand state-space for y_t, a_t as observed vars -- Use
239 | # stacking trick above
240 | Af = np.array([[ 1, 0, 0],
241 | [K1, 1 - K1, K1 * σ_y],
242 | [ 0, 0, 0]])
243 | Cf = np.array([[σ_x, 0],
244 | [ 0, K1 * σ_y],
245 | [ 0, 1]])
246 | Gf = np.array([[1, 0, σ_y],
247 | [1, -1, σ_y]])
248 |
249 | μ_true, μ_prior = 10, 10
250 | μ_f = np.array([μ_true, μ_prior, 0]).reshape(3, 1)
251 |
252 | # Create the state-space
253 | ssf = LinearStateSpace(Af, Cf, Gf, mu_0=μ_f)
254 |
255 | # Draw observations of y from the state-space model
256 | N = 50
257 | xf, yf = ssf.simulate(N)
258 |
259 | print(f"Kalman gain = {K1}")
260 | print(f"Conditional variance = {S1}")
261 | ```
262 |
263 | Now that we have simulated our joint system, we have $x_t$,
264 | $\hat{x_t}$, and $y_t$.
265 |
266 | We can now investigate how these
267 | variables are related by plotting some key objects.
268 |
269 | ## Estimates of unobservables
270 |
271 | First, let’s plot the hidden state $x_t$ and the filtered version
272 | $\hat x_t$ that is linear-least squares projection of $x_t$
273 | on the history $y_{t-1}, y_{t-2}, \ldots$
274 |
275 | ```{code-cell} python3
276 | fig, ax = plt.subplots()
277 | ax.plot(xf[0, :], label="$x_t$")
278 | ax.plot(xf[1, :], label="Filtered $x_t$")
279 | ax.legend()
280 | ax.set_xlabel("Time")
281 | ax.set_title(r"$x$ vs $\hat{x}$")
282 | plt.show()
283 | ```
284 |
285 | Note how $x_t$ and $\hat{x_t}$ differ.
286 |
287 | For Friedman, $\hat x_t$ and not $x_t$ is the consumer’s
288 | idea about her/his *permanent income*.
289 |
290 | ## Relationship of unobservables to observables
291 |
292 | Now let’s plot $x_t$ and $y_t$.
293 |
294 | Recall that $y_t$ is just $x_t$ plus white noise
295 |
296 | ```{code-cell} python3
297 | fig, ax = plt.subplots()
298 | ax.plot(yf[0, :], label="y")
299 | ax.plot(xf[0, :], label="x")
300 | ax.legend()
301 | ax.set_title(r"$x$ and $y$")
302 | ax.set_xlabel("Time")
303 | plt.show()
304 | ```
305 |
306 | We see above that $y$ seems to look like white noise around the
307 | values of $x$.
308 |
309 | ### Innovations
310 |
311 | Recall that we wrote down the innovation representation that depended on
312 | $a_t$. We now plot the innovations $\{a_t\}$:
313 |
314 | ```{code-cell} python3
315 | fig, ax = plt.subplots()
316 | ax.plot(yf[1, :], label="a")
317 | ax.legend()
318 | ax.set_title(r"Innovation $a_t$")
319 | ax.set_xlabel("Time")
320 | plt.show()
321 | ```
322 |
323 | ## MA and AR representations
324 |
325 | Now we shall extract from the `Kalman` instance `kmuth` coefficients of
326 |
327 | - a fundamental moving average representation that represents
328 | $y_t$ as a one-sided moving sum of current and past
329 | $a_t$s that are square summable linear combinations of $y_t, y_{t-1}, \ldots$.
330 | - a univariate autoregression representation that depicts the
331 | coefficients in a linear least square projection of $y_t$ on
332 | the semi-infinite history $y_{t-1}, y_{t-2}, \ldots$.
333 |
334 | Then we’ll plot each of them
335 |
336 | ```{code-cell} python3
337 | # Kalman Methods for MA and VAR
338 | coefs_ma = kmuth.stationary_coefficients(5, "ma")
339 | coefs_var = kmuth.stationary_coefficients(5, "var")
340 |
341 | # Coefficients come in a list of arrays, but we
342 | # want to plot them and so need to stack into an array
343 | coefs_ma_array = np.vstack(coefs_ma)
344 | coefs_var_array = np.vstack(coefs_var)
345 |
346 | fig, ax = plt.subplots(2)
347 | ax[0].plot(coefs_ma_array, label="MA")
348 | ax[0].legend()
349 | ax[1].plot(coefs_var_array, label="VAR")
350 | ax[1].legend()
351 |
352 | plt.show()
353 | ```
354 |
355 | The **moving average** coefficients in the top panel show tell-tale
356 | signs of $y_t$ being a process whose first difference is a first-order
357 | autoregression.
358 |
359 | The **autoregressive coefficients** decline geometrically with decay
360 | rate $(1-K)$.
361 |
362 | These are exactly the target outcomes that Muth (1960) aimed to reverse
363 | engineer
364 |
365 | ```{code-cell} python3
366 | print(f'decay parameter 1 - K1 = {1 - K1}')
367 | ```
368 |
--------------------------------------------------------------------------------
/lectures/cattle_cycles.md:
--------------------------------------------------------------------------------
1 | ---
2 | jupytext:
3 | text_representation:
4 | extension: .md
5 | format_name: myst
6 | kernelspec:
7 | display_name: Python 3
8 | language: python
9 | name: python3
10 | ---
11 |
12 | (cattle_cycles)=
13 | ```{raw} html
14 |
19 | ```
20 |
21 | ```{index} single: python
22 | ```
23 |
24 | # Cattle Cycles
25 |
26 | This is another member of a suite of lectures that use the quantecon DLE class to instantiate models within the
27 | {cite}`HS2013` class of models described in detail in {doc}`Recursive Models of Dynamic Linear Economies `.
28 |
29 | In addition to what's in Anaconda, this lecture uses the quantecon library.
30 |
31 | ```{code-cell} ipython
32 | ---
33 | tags: [hide-output]
34 | ---
35 | !pip install --upgrade quantecon
36 | ```
37 |
38 | This lecture uses the DLE class to construct instances of the "Cattle Cycles" model
39 | of Rosen, Murphy and Scheinkman (1994) {cite}`rosen1994cattle`.
40 |
41 | That paper constructs a rational expectations equilibrium model to
42 | understand sources of recurrent cycles in US cattle stocks and prices.
43 |
44 | We make the following imports:
45 |
46 | ```{code-cell} ipython
47 | import numpy as np
48 | import matplotlib.pyplot as plt
49 | from collections import namedtuple
50 | from quantecon import DLE
51 | from math import sqrt
52 | ```
53 |
54 | ## The model
55 |
56 | The model features a static linear demand curve and a "time-to-grow"
57 | structure for cattle.
58 |
59 | Let $p_t$ be the price of slaughtered beef, $m_t$ the cost
60 | of preparing an animal for slaughter, $h_t$ the holding cost for a
61 | mature animal, $\gamma_1 h_t$ the holding cost for a yearling, and
62 | $\gamma_0 h_t$ the holding cost for a calf.
63 |
64 | The cost processes $\{h_t, m_t \}_{t=0}^\infty$ are exogenous,
65 | while the price process $\{p_t \}_{t=0}^\infty$ is determined
66 | within a rational expectations equilibrium.
67 |
68 | Let $x_t$ be the breeding stock, and $y_t$ be the total
69 | stock of cattle.
70 |
71 | The law of motion for the breeding stock is
72 |
73 | $$
74 | x_t = (1-\delta)x_{t-1} + gx_{t-3} - c_t
75 | $$
76 |
77 | where $g < 1$ is the number of calves that each member of the
78 | breeding stock has each year, and $c_t$ is the number of cattle
79 | slaughtered.
80 |
81 | The total headcount of cattle is
82 |
83 | $$
84 | y_t = x_t + gx_{t-1} + gx_{t-2}
85 | $$
86 |
87 | This equation states that the total number of cattle equals the sum of
88 | adults, calves and yearlings, respectively.
89 |
90 | A representative farmer chooses $\{c_t, x_t\}$ to maximize:
91 |
92 | $$
93 | \mathbb{E}_0 \sum_{t=0}^\infty \beta^t \{p_tc_t - h_tx_t - \gamma_0 h_t(gx_{t-1}) - \gamma_1 h_t(gx_{t-2}) - m_tc_t - \frac{\psi_1}{2}x_t^2 - \frac{\psi_2}{2}x_{t-1}^2 - \frac{\psi_3}{2}x_{t-3}^2 - \frac{\psi_4}{2}c_t^2 \}
94 | $$
95 |
96 | subject to the law of motion for $x_t$, taking as given the
97 | stochastic laws of motion for the exogenous processes, the equilibrium
98 | price process, and the initial state [$x_{-1},x_{-2},x_{-3}$].
99 |
100 | **Remark** The $\psi_j$ parameters are very small quadratic costs
101 | that are included for technical reasons to make well posed and well
102 | behaved the linear quadratic dynamic programming problem solved by the
103 | fictitious planner who in effect chooses equilibrium quantities and shadow
104 | prices.
105 |
106 | Demand for beef is government by $c_t = a_0 - a_1p_t + \tilde d_t$
107 | where $\tilde d_t$ is a stochastic process with mean zero,
108 | representing a demand shifter.
109 |
110 | ## Mapping into HS2013 framework
111 |
112 | ### Preferences
113 |
114 | We set
115 | $\Lambda = 0, \Delta_h = 0, \Theta_h = 0, \Pi = \alpha_1^{-\frac{1}{2}}$
116 | and $b_t = \Pi \tilde d_t + \Pi \alpha_0$.
117 |
118 | With these settings, the FOC for the household's problem becomes the
119 | demand curve of the "Cattle Cycles" model.
120 |
121 | ### Technology
122 |
123 | To capture the law of motion for cattle, we set
124 |
125 | $$
126 | \Delta_k =
127 | \left[ {\begin{array}{ccc}
128 | (1-\delta) & 0 & g \\ 1 & 0 & 0 \\ 0 & 1 & 0
129 | \end{array} }
130 | \right] , \
131 | \Theta_k =
132 | \left[ {\begin{array}{c}
133 | 1 \\ 0 \\ 0
134 | \end{array} }
135 | \right]
136 | $$
137 |
138 | (where $i_t = - c_t$).
139 |
140 | To capture the production of cattle, we set
141 |
142 | $$
143 | \Phi_c =
144 | \left[ {\begin{array}{c}
145 | 1 \\ f_1 \\ 0 \\ 0 \\ -f_7
146 | \end{array} }
147 | \right] , \
148 | \Phi_g =
149 | \left[ {\begin{array}{cccc}
150 | 0 & 0 & 0 & 0 \\ 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1
151 | \end{array} }
152 | \right] , \
153 | \Phi_i =
154 | \left[ {\begin{array}{c}
155 | 1 \\ 0 \\ 0 \\ 0 \\ 0
156 | \end{array} }
157 | \right] , \
158 | \Gamma =
159 | \left[ {\begin{array}{ccc}
160 | 0 & 0 & 0 \\ f_1(1-\delta) & 0 & g f_1 \\ f_3 & 0 & 0 \\ 0 & f_5 & 0 \\ 0 & 0 & 0
161 | \end{array} }
162 | \right]
163 | $$
164 |
165 | ### Information
166 |
167 | We set
168 |
169 | $$
170 | A_{22} =
171 | \left[ {\begin{array}{cccc}
172 | 1 & 0 & 0 & 0\\ 0 & \rho_1 & 0 & 0 \\ 0 & 0 & \rho_2 & 0 \\ 0 & 0 & 0 & \rho_3
173 | \end{array} }
174 | \right] \
175 | , C_2 =
176 | \left[ {\begin{array}{ccc}
177 | 0 & 0 & 0\\ 1 & 0 & 0 \\ 0 & 1 & 0 \\ 0 & 0 & 15
178 | \end{array} }
179 | \right] \
180 | , U_b =
181 | \left[ {\begin{array}{cccc}
182 | \Pi \alpha_0 & 0 & 0 & \Pi
183 | \end{array} }
184 | \right] \
185 | , U_d =
186 | \left[ {\begin{array}{c}
187 | 0 \\ f_2 U_h \\ f_4 U_h \\ f_6 U_h \\ f_8 U_h
188 | \end{array} }
189 | \right]
190 | $$
191 |
192 | To map this into our class, we set $f_1^2 = \frac{\Psi_1}{2}$,
193 | $f_2^2 = \frac{\Psi_2}{2}$, $f_3^2 = \frac{\Psi_3}{2}$,
194 | $2f_1f_2 = 1$, $2f_3f_4 = \gamma_0g$,
195 | $2f_5f_6 = \gamma_1g$.
196 |
197 | ```{code-cell} python3
198 | # We define namedtuples in this way as it allows us to check, for example,
199 | # what matrices are associated with a particular technology.
200 |
201 | Information = namedtuple('Information', ['a22', 'c2', 'ub', 'ud'])
202 | Technology = namedtuple('Technology', ['ϕ_c', 'ϕ_g', 'ϕ_i', 'γ', 'δ_k', 'θ_k'])
203 | Preferences = namedtuple('Preferences', ['β', 'l_λ', 'π_h', 'δ_h', 'θ_h'])
204 | ```
205 |
206 | We set parameters to those used by {cite}`rosen1994cattle`
207 |
208 | ```{code-cell} python3
209 | β = np.array([[0.909]])
210 | lλ = np.array([[0]])
211 |
212 | a1 = 0.5
213 | πh = np.array([[1 / (sqrt(a1))]])
214 | δh = np.array([[0]])
215 | θh = np.array([[0]])
216 |
217 | δ = 0.1
218 | g = 0.85
219 | f1 = 0.001
220 | f3 = 0.001
221 | f5 = 0.001
222 | f7 = 0.001
223 |
224 | ϕc = np.array([[1], [f1], [0], [0], [-f7]])
225 |
226 | ϕg = np.array([[0, 0, 0, 0],
227 | [1, 0, 0, 0],
228 | [0, 1, 0, 0],
229 | [0, 0, 1,0],
230 | [0, 0, 0, 1]])
231 |
232 | ϕi = np.array([[1], [0], [0], [0], [0]])
233 |
234 | γ = np.array([[ 0, 0, 0],
235 | [f1 * (1 - δ), 0, g * f1],
236 | [ f3, 0, 0],
237 | [ 0, f5, 0],
238 | [ 0, 0, 0]])
239 |
240 | δk = np.array([[1 - δ, 0, g],
241 | [ 1, 0, 0],
242 | [ 0, 1, 0]])
243 |
244 | θk = np.array([[1], [0], [0]])
245 |
246 | ρ1 = 0
247 | ρ2 = 0
248 | ρ3 = 0.6
249 | a0 = 500
250 | γ0 = 0.4
251 | γ1 = 0.7
252 | f2 = 1 / (2 * f1)
253 | f4 = γ0 * g / (2 * f3)
254 | f6 = γ1 * g / (2 * f5)
255 | f8 = 1 / (2 * f7)
256 |
257 | a22 = np.array([[1, 0, 0, 0],
258 | [0, ρ1, 0, 0],
259 | [0, 0, ρ2, 0],
260 | [0, 0, 0, ρ3]])
261 |
262 | c2 = np.array([[0, 0, 0],
263 | [1, 0, 0],
264 | [0, 1, 0],
265 | [0, 0, 15]])
266 |
267 | πh_scalar = πh.item()
268 | ub = np.array([[πh_scalar * a0, 0, 0, πh_scalar]])
269 | uh = np.array([[50, 1, 0, 0]])
270 | um = np.array([[100, 0, 1, 0]])
271 | ud = np.vstack(([0, 0, 0, 0],
272 | f2 * uh, f4 * uh, f6 * uh, f8 * um))
273 | ```
274 |
275 | Notice that we have set $\rho_1 = \rho_2 = 0$, so $h_t$ and
276 | $m_t$ consist of a constant and a white noise component.
277 |
278 | We set up the economy using tuples for information, technology and
279 | preference matrices below.
280 |
281 | We also construct two extra information matrices, corresponding to cases
282 | when $\rho_3 = 1$ and $\rho_3 = 0$ (as opposed to the
283 | baseline case of $\rho_3 = 0.6$).
284 |
285 | ```{code-cell} python3
286 | info1 = Information(a22, c2, ub, ud)
287 | tech1 = Technology(ϕc, ϕg, ϕi, γ, δk, θk)
288 | pref1 = Preferences(β, lλ, πh, δh, θh)
289 |
290 | ρ3_2 = 1
291 | a22_2 = np.array([[1, 0, 0, 0],
292 | [0, ρ1, 0, 0],
293 | [0, 0, ρ2, 0],
294 | [0, 0, 0, ρ3_2]])
295 |
296 | info2 = Information(a22_2, c2, ub, ud)
297 |
298 | ρ3_3 = 0
299 | a22_3 = np.array([[1, 0, 0, 0],
300 | [0, ρ1, 0, 0],
301 | [0, 0, ρ2, 0],
302 | [0, 0, 0, ρ3_3]])
303 |
304 | info3 = Information(a22_3, c2, ub, ud)
305 |
306 | # Example of how we can look at the matrices associated with a given namedtuple
307 | info1.a22
308 | ```
309 |
310 | ```{code-cell} python3
311 | # Use tuples to define DLE class
312 | econ1 = DLE(info1, tech1, pref1)
313 | econ2 = DLE(info2, tech1, pref1)
314 | econ3 = DLE(info3, tech1, pref1)
315 |
316 | # Calculate steady-state in baseline case and use to set the initial condition
317 | econ1.compute_steadystate(nnc=4)
318 | x0 = econ1.zz
319 | ```
320 |
321 | ```{code-cell} python3
322 | econ1.compute_sequence(x0, ts_length=100)
323 | ```
324 |
325 | {cite}`rosen1994cattle` use the model to understand the
326 | sources of recurrent cycles in total cattle stocks.
327 |
328 | Plotting $y_t$ for a simulation of their model shows its ability
329 | to generate cycles in quantities
330 |
331 | ```{code-cell} python3
332 | # Calculation of y_t
333 | totalstock = econ1.k[0] + g * econ1.k[1] + g * econ1.k[2]
334 | fig, ax = plt.subplots()
335 | ax.plot(totalstock)
336 | ax.set_xlim((-1, 100))
337 | ax.set_title('Total number of cattle')
338 | plt.show()
339 | ```
340 |
341 | In their Figure 3, {cite}`rosen1994cattle` plot the impulse response functions
342 | of consumption and the breeding stock of cattle to the demand shock,
343 | $\tilde d_t$, under the three different values of $\rho_3$.
344 |
345 | We replicate their Figure 3 below
346 |
347 | ```{code-cell} python3
348 | shock_demand = np.array([[0], [0], [1]])
349 |
350 | econ1.irf(ts_length=25, shock=shock_demand)
351 | econ2.irf(ts_length=25, shock=shock_demand)
352 | econ3.irf(ts_length=25, shock=shock_demand)
353 |
354 | fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 4))
355 | ax1.plot(econ1.c_irf, label=r'$\rho=0.6$')
356 | ax1.plot(econ2.c_irf, label=r'$\rho=1$')
357 | ax1.plot(econ3.c_irf, label=r'$\rho=0$')
358 | ax1.set_title('Consumption response to demand shock')
359 | ax1.legend()
360 |
361 | ax2.plot(econ1.k_irf[:, 0], label=r'$\rho=0.6$')
362 | ax2.plot(econ2.k_irf[:, 0], label=r'$\rho=1$')
363 | ax2.plot(econ3.k_irf[:, 0], label=r'$\rho=0$')
364 | ax2.set_title('Breeding stock response to demand shock')
365 | ax2.legend()
366 | plt.show()
367 | ```
368 |
369 | The above figures show how consumption patterns differ markedly,
370 | depending on the persistence of the demand shock:
371 |
372 | - If it is purely transitory ($\rho_3 = 0$) then consumption
373 | rises immediately but is later reduced to build stocks up again.
374 | - If it is permanent ($\rho_3 = 1$), then consumption falls
375 | immediately, in order to build up stocks to satisfy the permanent
376 | rise in future demand.
377 |
378 | In Figure 4 of their paper, {cite}`rosen1994cattle` plot the response to a demand shock
379 | of the breeding stock *and* the total stock, for $\rho_3 = 0$ and
380 | $\rho_3 = 0.6$.
381 |
382 | We replicate their Figure 4 below
383 |
384 | ```{code-cell} python3
385 | total1_irf = econ1.k_irf[:, 0] + g * econ1.k_irf[:, 1] + g * econ1.k_irf[:, 2]
386 | total3_irf = econ3.k_irf[:, 0] + g * econ3.k_irf[:, 1] + g * econ3.k_irf[:, 2]
387 |
388 | fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 4))
389 | ax1.plot(econ1.k_irf[:, 0], label='Breeding Stock')
390 | ax1.plot(total1_irf, label='Total Stock')
391 | ax1.set_title(r'$\rho=0.6$')
392 |
393 | ax2.plot(econ3.k_irf[:, 0], label='Breeding Stock')
394 | ax2.plot(total3_irf, label='Total Stock')
395 | ax2.set_title(r'$\rho=0$')
396 | plt.show()
397 | ```
398 |
399 | The fact that $y_t$ is a weighted moving average of $x_t$
400 | creates a humped shape response of the total stock in response to demand
401 | shocks, contributing to the cyclicality seen in the first graph of this
402 | lecture.
403 |
--------------------------------------------------------------------------------
/lectures/_static/lecture_specific/match_transport/acs_data_summary.csv:
--------------------------------------------------------------------------------
1 | count,mean_Earnings,std_Earnings
2 | 1331,14900.405709992487,16432.77000114295
3 | 113,14945.044247787611,16555.977239779477
4 | 536,16235.858208955224,14390.705658707586
5 | 3788,16620.493928194297,15627.600938733565
6 | 1337,16868.38444278235,20674.271930182003
7 | 1455,17200.240549828177,15666.75943016007
8 | 9900,17485.504646464648,16898.448536979464
9 | 413,17855.375302663437,24934.411743555444
10 | 317,18443.72239747634,18910.04500054648
11 | 12370,18945.816572352465,21375.376002331996
12 | 12031,19723.61815310448,19450.33475965967
13 | 8501,19992.385601693917,26313.652148468223
14 | 8800,20444.996704545454,16164.839628000927
15 | 773,20644.954721862872,13843.979122054649
16 | 7403,20739.541267053897,21147.46175638877
17 | 583,21070.686106346482,13184.831707584104
18 | 275,21266.98181818182,14862.656038103265
19 | 1359,21793.024282560706,18438.68669968874
20 | 2191,21903.929712460063,19776.452810936185
21 | 58,22020.344827586207,11427.21619881441
22 | 2745,22091.814207650274,18025.068526790623
23 | 905,22172.475138121546,26617.647130774334
24 | 654,23896.785932721712,26438.239579467132
25 | 1373,24324.493809176984,21485.325471629778
26 | 4016,24728.254482071712,19186.253404900126
27 | 59,24757.627118644068,13522.576312760239
28 | 1808,24881.766592920354,21619.594437915246
29 | 5611,25004.48957405097,23614.704606262276
30 | 968,25148.421487603307,38377.07442008185
31 | 844,25229.348341232228,29279.511776925534
32 | 276,25384.565217391304,16203.599964522593
33 | 78,25448.71794871795,11606.690229141901
34 | 445,25581.460674157304,27127.679937797322
35 | 7586,25584.14909042974,23230.713830277662
36 | 6467,26015.113653935365,20486.78966825072
37 | 362,26178.701657458565,36192.85405241237
38 | 455,26305.69230769231,36790.92885699715
39 | 2155,26422.19489559165,22525.939195468625
40 | 261,26515.708812260535,18824.594468394233
41 | 193,26571.709844559584,33818.247365493444
42 | 15991,26618.94715777625,25397.867018966135
43 | 8857,26899.661736479622,25346.770850944646
44 | 86,26936.04651162791,18352.143088771747
45 | 189,27107.989417989418,21099.854849251024
46 | 249,27184.176706827307,39977.39939420132
47 | 295,27234.20338983051,21839.87282143491
48 | 235,27331.063829787236,16073.20635525605
49 | 611,27346.85761047463,16419.357945885145
50 | 327,27385.443425076453,17875.40932739134
51 | 16,27528.125,22813.8113778328
52 | 2640,27693.99659090909,26681.361802938936
53 | 166,27747.349397590362,22331.91891953525
54 | 515,27771.844660194176,25404.165049131756
55 | 2467,27878.57965139846,26307.11527752712
56 | 481,28258.89812889813,16958.543502956967
57 | 3803,28274.057323165922,21546.587241382982
58 | 267,28470.674157303372,23340.82883106868
59 | 67,28489.10447761194,46698.42203327656
60 | 364,28600.274725274725,44244.289584147
61 | 266,28997.48120300752,15988.964261640675
62 | 90,29713.333333333332,20165.735763328958
63 | 51,29776.470588235294,11574.29199969128
64 | 81,29792.716049382718,22010.41747743246
65 | 801,29881.373283395755,20583.989667013906
66 | 1021,30278.530852105778,23218.795813219058
67 | 177,30332.20338983051,17935.78926601116
68 | 486,30465.637860082305,28259.00371625996
69 | 3405,30653.36563876652,23401.127379700003
70 | 422,30680.52132701422,27465.065363194262
71 | 227,30769.471365638765,42474.80173020462
72 | 246,30885.731707317074,55546.60613517166
73 | 13320,31045.933183183184,26946.80530452291
74 | 1627,31137.301782421633,24389.434720529644
75 | 2115,31139.739952718675,24737.420133310752
76 | 690,31313.75507246377,39546.37376933845
77 | 234,31333.076923076922,19440.086409031836
78 | 3528,31353.257369614512,30201.88617852633
79 | 55,31432.727272727272,17733.19814636707
80 | 2556,31740.395148669795,23858.236519580147
81 | 226,31883.893805309734,20194.11051365129
82 | 4184,31913.109464627152,20005.96503471116
83 | 3216,32061.293843283584,37659.195701316865
84 | 115,32449.565217391304,33948.000416305025
85 | 124,32482.016129032258,30264.572488880414
86 | 1571,32499.86632718014,22220.855809623023
87 | 459,32765.054466230937,29315.317406751692
88 | 1229,32881.47111472742,30461.849977536294
89 | 214,32882.056074766355,29665.01036379667
90 | 70,32884.28571428572,16767.59846185654
91 | 216,33215.416666666664,40694.92326274207
92 | 2473,33393.8819247877,23015.060530423583
93 | 8822,33526.67671729766,27492.126718365995
94 | 4217,33726.49276737017,26718.033992494773
95 | 307,33792.44299674267,26712.79555993049
96 | 3990,34137.160401002504,22439.7319837709
97 | 229,34149.34497816594,40238.782484030686
98 | 7184,34210.84632516704,26237.520916352805
99 | 80,34211.25,18678.499070633203
100 | 257,34308.21011673152,23132.80010145342
101 | 371,34429.757412398925,37069.96194480972
102 | 941,34452.96493092455,25627.40342289543
103 | 5733,34578.5853828711,32184.155611732942
104 | 127,34662.04724409449,17078.78696012971
105 | 444,34668.80630630631,37250.29975389088
106 | 927,34846.03020496224,26688.921318718658
107 | 898,34897.29510022272,36904.565568355814
108 | 912,35151.304824561405,37031.80265454963
109 | 11425,35212.49426695843,32120.41867256671
110 | 3685,35402.0461329715,23899.45809249939
111 | 935,35720.95935828877,31137.61957437525
112 | 25472,35847.70846419598,27026.4837865098
113 | 125,35863.36,32142.15057301027
114 | 1092,36288.919413919415,19371.86049014382
115 | 69,36692.76811594203,29093.918151669208
116 | 74,36800.0,26658.59752345963
117 | 6188,36829.22915319974,27642.010924837155
118 | 16323,36919.59504992955,36092.85778402987
119 | 814,37040.78746928747,20638.92809201809
120 | 260,37185.846153846156,30210.24643442601
121 | 5042,37199.77072590242,37321.19404953267
122 | 517,37514.85493230174,26726.8595597498
123 | 17530,37572.28807758129,46212.2496496395
124 | 9662,37583.05837300766,29650.724538505343
125 | 375,37587.706666666665,27981.512490403795
126 | 698,37674.140401146135,24760.513994609373
127 | 9364,38058.768154634774,29711.909070934867
128 | 605,38081.90082644628,23280.336767112032
129 | 3214,38167.74113254512,23812.013413820972
130 | 97,38174.742268041235,17783.972951601645
131 | 94,38236.17021276596,42287.02450030028
132 | 194,38319.58762886598,19153.0575714908
133 | 8294,38398.73306004341,30491.30807084761
134 | 558,38404.74910394265,26292.91925062153
135 | 1159,38474.03019844694,42600.93738514701
136 | 1208,38751.82698675497,54174.68548176281
137 | 746,38856.635388739945,40640.23796181628
138 | 1084,39038.4778597786,28601.32669679299
139 | 933,39267.71918542337,32088.15983517525
140 | 643,39439.28460342146,20534.148204346457
141 | 529,39501.32325141777,36024.72238957143
142 | 1391,39717.54852624011,31001.739732344027
143 | 363,39813.691460055095,23568.937152323517
144 | 381,39837.40157480315,29296.054715859427
145 | 348,40133.96551724138,21622.060389069033
146 | 229,40141.18340611354,34957.815699927174
147 | 1374,40187.91848617176,25394.113583574104
148 | 265,40230.83018867925,49287.03812706216
149 | 423,40320.3073286052,35871.12588392239
150 | 843,40352.9418742586,34984.772515512734
151 | 207,40529.371980676326,18488.14469501662
152 | 668,40725.37425149701,28373.754221909596
153 | 431,40830.533642691415,31645.303379706
154 | 280,40862.0,41453.58181687112
155 | 1334,41027.22488755622,21706.293340332068
156 | 111,41052.252252252256,21810.659807646378
157 | 4703,41081.73931533064,30155.6280550901
158 | 655,41335.87786259542,27797.56459945549
159 | 2204,41376.19328493648,27692.15659180858
160 | 175,41430.857142857145,26229.435663736567
161 | 6045,41482.893300248135,36341.09627465994
162 | 600,41527.473333333335,46211.735299325366
163 | 2916,41577.743484224964,31769.736154906233
164 | 691,41609.75397973951,29195.504519399656
165 | 221,41659.72850678733,23336.66755672774
166 | 964,41809.242738589215,25307.404897071097
167 | 257,42811.284046692606,35015.27999474527
168 | 175,42911.08571428571,23269.90880323775
169 | 192,43063.541666666664,21051.575259319146
170 | 117,43194.8717948718,20689.856110872162
171 | 636,43281.588050314465,20160.23022366003
172 | 1489,43330.597716588316,40353.07331609059
173 | 277,43351.73285198556,24633.325751226825
174 | 64,43659.375,18341.753207212383
175 | 197,43745.93908629441,27462.099741286213
176 | 888,43828.04054054054,28074.22863018439
177 | 1069,44094.79981290926,29729.09800287853
178 | 242,44177.396694214876,26173.4245868871
179 | 91,44213.18681318681,26434.641450571416
180 | 272,44218.82352941176,29516.530737921956
181 | 167,44351.796407185626,58675.72744099032
182 | 4611,44567.13294296248,30221.6451884297
183 | 2132,44736.44512195122,31898.371997271137
184 | 198,44871.919191919194,46820.006985967295
185 | 1324,44981.56344410876,26774.104176795416
186 | 5914,45026.84984781873,32824.83888195073
187 | 66,45305.15151515151,28342.115725944735
188 | 97,45441.237113402065,21805.325957670862
189 | 745,45480.040268456374,24964.540446454812
190 | 24474,45618.37149628177,37652.8508249016
191 | 4008,45730.57634730539,25712.302187776186
192 | 184,45741.30434782609,31541.84367018702
193 | 1483,45752.27242076871,33880.20921041752
194 | 292,45850.68493150685,35447.424630448375
195 | 205,46430.73170731707,32978.16392752343
196 | 1987,46552.1791645697,24796.0699878023
197 | 480,46663.333333333336,19953.078511888845
198 | 790,46709.91139240506,26102.47071751405
199 | 724,47023.4544198895,33101.32035161209
200 | 2333,47139.80325760823,27632.913326289738
201 | 2760,47171.37463768116,29721.461675688308
202 | 132,47271.969696969696,18757.46493391808
203 | 420,47279.333333333336,28742.175401487362
204 | 1344,47448.56398809524,34781.02022125067
205 | 2409,47598.87920298879,33112.226785536164
206 | 1182,47735.897631133674,53417.473248027534
207 | 2753,48275.37595350527,25392.883035987077
208 | 355,48282.81690140845,48664.66383493207
209 | 920,48287.891304347824,24054.77273442469
210 | 30711,48485.85624043502,25974.994170520782
211 | 1510,48623.9821192053,58496.75850772877
212 | 912,48924.59429824561,45467.12428353692
213 | 1431,49157.00908455625,67020.1922750646
214 | 182,49536.26373626374,26353.35749305159
215 | 2785,49738.92315978456,22858.619677648265
216 | 3355,49961.701937406855,27507.04969097614
217 | 2910,50379.9587628866,32544.59237225434
218 | 85,50483.529411764706,48670.047616722186
219 | 352,50664.28977272727,39921.32811931963
220 | 431,50763.96751740139,36132.32513053202
221 | 727,50929.40852819807,26898.772377839156
222 | 2581,50996.691204959316,34830.513679188836
223 | 1558,51065.64184852375,46185.647507106645
224 | 3991,51556.95088950138,44608.90529421183
225 | 6397,52829.89057370643,26272.447766654015
226 | 1298,52961.702619414486,30427.788117067503
227 | 3310,53273.045317220545,27325.337718782423
228 | 255,53495.686274509804,40788.49735834336
229 | 1766,54103.9637599094,36140.820368584136
230 | 1161,54156.770025839796,35941.12992327487
231 | 876,55171.57534246575,48064.29058816983
232 | 487,55279.05544147844,39941.18068275288
233 | 126,55473.01587301587,74930.57737687431
234 | 1692,55520.135933806145,37275.07901366944
235 | 6575,55656.67634980989,52923.80018420368
236 | 505,55812.158415841586,30225.258521198244
237 | 87,55926.4367816092,31894.668431653958
238 | 1195,55943.054393305436,26573.791302972855
239 | 60,56070.0,27083.169456262134
240 | 2927,56210.256235052955,37252.18885203767
241 | 104,56230.730769230766,22789.881803682973
242 | 452,56331.50442477876,54095.804251743946
243 | 176,57216.47727272727,36990.113135879896
244 | 445,57314.831460674155,45440.10219663298
245 | 2363,57923.77486246297,39485.808205901696
246 | 131,58089.31297709924,25045.909390890094
247 | 2924,58194.476744186046,30824.85153938926
248 | 318,58424.55974842767,89388.3005190242
249 | 271,58573.87453874539,44868.13525051241
250 | 162,58727.16049382716,30604.836675139595
251 | 1418,58852.99788434415,53490.17855659326
252 | 71,58945.77464788732,48006.99260560076
253 | 569,59005.92267135325,32993.73118382685
254 | 756,59329.40476190476,48712.277259477036
255 | 322,59479.19254658385,34228.098283699226
256 | 199,59486.38190954774,32873.24013763075
257 | 2285,59627.90371991247,44712.67296198911
258 | 1005,59827.164179104475,45586.46427960656
259 | 62,60098.3870967742,53146.91821158291
260 | 185,60263.24324324324,55709.45347798263
261 | 1346,60270.31203566122,47423.885049741664
262 | 254,60493.700787401576,37174.341416732925
263 | 596,61021.54362416107,43967.92213390216
264 | 166,61025.301204819276,54169.58744954755
265 | 367,61102.12534059946,25381.578543313
266 | 217,61188.01843317972,57184.118199863464
267 | 268,61353.35820895522,34154.96997069272
268 | 10816,61562.85965236687,55996.039197531405
269 | 259,61680.50193050193,67521.4391458056
270 | 155,61690.32258064516,29792.071145558497
271 | 1712,62005.94976635514,82004.00011630662
272 | 651,62063.133640552995,57119.25601766312
273 | 704,62695.923295454544,51568.97804067012
274 | 449,62741.98218262806,68033.4473468201
275 | 1025,62830.55609756098,45748.607652384984
276 | 587,63250.68143100511,62428.68316557238
277 | 735,63420.91156462585,32404.27322144226
278 | 162,63453.7037037037,31927.773962062136
279 | 302,63934.17218543046,48465.51401749722
280 | 663,64399.034690799395,95690.3002845804
281 | 388,64507.47422680412,32780.25924528305
282 | 671,64980.76005961252,39938.30308490889
283 | 477,65159.95807127882,40091.931783288856
284 | 212,65817.45283018867,41448.393026466496
285 | 561,65985.20499108735,56380.00701872179
286 | 1071,67327.55368814192,42492.081956091955
287 | 1954,67511.62538382804,71960.90116183538
288 | 5979,67784.04749958187,33363.825168154886
289 | 5857,67795.41061977121,60494.66027027032
290 | 525,68352.09523809524,42952.24731066717
291 | 109,68780.73394495413,29726.978260384523
292 | 443,69287.47178329571,32411.255550407794
293 | 236,70715.25423728813,64920.68762121735
294 | 457,71396.3238512035,49319.07527537085
295 | 1476,71814.65447154471,53727.080294440704
296 | 201,72201.99004975124,42954.59342165748
297 | 889,72983.59955005624,52618.60901769051
298 | 3955,73775.20960809103,83809.58210059197
299 | 2484,74696.73510466989,72022.22140203201
300 | 981,76734.77064220184,40100.00367796557
301 | 5456,76824.88178152492,94015.14422905697
302 | 1920,78107.23958333333,61634.14362029448
303 | 1070,78638.73831775702,36784.3423953724
304 | 999,79330.23023023023,92468.32050622275
305 | 256,79656.6015625,43021.64179250923
306 | 691,80269.60926193921,54467.91771833894
307 | 1949,80882.58081067214,61989.21190635767
308 | 133,81566.31578947368,36265.32857673165
309 | 344,81724.04069767441,70318.5743263255
310 | 396,81917.67676767676,54724.49654573229
311 | 199,82389.94974874372,37013.348663561796
312 | 405,82656.04938271605,31312.455912066045
313 | 1242,83829.34782608696,43418.88769695796
314 | 11079,84068.53678129795,78540.05144529899
315 | 945,84390.61375661376,42853.130060461604
316 | 154,85905.84415584416,66653.03119086035
317 | 1038,85910.73217726397,92576.05740925772
318 | 312,87788.94230769231,79559.47348408098
319 | 4880,88004.68032786885,92082.56051412193
320 | 2025,88097.89135802469,43108.77694566431
321 | 77,88574.02597402598,58833.73853629576
322 | 478,88867.78242677824,36799.042458869335
323 | 1250,88905.3584,93591.33823782875
324 | 1273,89346.09269442262,74507.52912062789
325 | 359,89509.55431754874,54599.991408533984
326 | 1482,90342.52091767882,79346.52467286953
327 | 97,90355.67010309278,34388.97282400326
328 | 460,91395.43478260869,38724.948762694294
329 | 831,92410.26474127558,55721.05171320993
330 | 2570,92907.28171206226,57043.36881076245
331 | 603,93742.68656716419,85058.5568150245
332 | 438,95017.35159817351,79864.18001371343
333 | 105,98062.85714285714,65425.62168239192
334 | 453,98758.25607064017,116134.01267427258
335 | 292,99711.98630136986,53726.51594537541
336 | 4496,100022.6859430605,56900.20721783729
337 | 1690,102356.9349112426,56626.570667655185
338 | 456,104803.48684210527,71311.26958940338
339 | 2404,106926.18552412646,63719.8232169186
340 | 71,110228.1690140845,73937.56668027153
341 | 304,113066.94078947368,64585.43476419163
342 | 505,113803.78217821782,60385.33964300992
343 | 1835,116122.87738419618,116295.86226115789
344 | 301,121848.67109634551,89534.09110628269
345 | 1130,130472.74336283185,97564.62817055204
346 | 337,145268.84272997032,105113.47047426814
347 | 2825,146967.46548672568,148283.85967018566
348 | 1874,151577.6152614728,163188.46424002026
349 | 8874,156727.0739238224,135097.47769384919
350 | 67,174783.58208955225,137227.9739979436
351 | 1224,188562.70506535948,141456.3948276375
352 | 7405,231459.67832545578,166980.91397517134
353 |
--------------------------------------------------------------------------------