├── .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 |
4 | 5 | QuantEcon 6 | 7 |
8 | -------------------------------------------------------------------------------- /lectures/_static/includes/header.raw: -------------------------------------------------------------------------------- 1 | .. raw:: html 2 | 3 |
4 | 5 | QuantEcon 6 | 7 |
8 | -------------------------------------------------------------------------------- /_notebook_repo/README.md: -------------------------------------------------------------------------------- 1 | # lecture-python-advanced.notebooks 2 | 3 | [![Binder](https://mybinder.org/badge_logo.svg)](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 |
15 | 16 | QuantEcon 17 | 18 |
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 |
15 | 16 | QuantEcon 17 | 18 |
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 |
15 | 16 | QuantEcon 17 | 18 |
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 |
15 | 16 | QuantEcon 17 | 18 |
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 |
15 | 16 | QuantEcon 17 | 18 |
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 |
15 | 16 | QuantEcon 17 | 18 |
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 |
15 | 16 | QuantEcon 17 | 18 |
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 |
15 | 16 | QuantEcon 17 | 18 |
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 |
15 | 16 | QuantEcon 17 | 18 |
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 | --------------------------------------------------------------------------------