├── .gitignore
├── 0 - Introduction
├── Introduction.ipynb
├── Introduction.lyx
├── Introduction.pdf
├── figs
│ ├── CEBI.png
│ ├── KUSAMFLogo.pdf
│ ├── KUSAMFtitlelrcorner.pdf
│ ├── ku_logo_dk_hh.png
│ └── ku_logo_uk_v.png
└── mymodule.py
├── 1 - ASAD
├── ASAD.ipynb
└── ASAD.py
├── 1 - Atomic types
├── 1 - Atomic types.ipynb
└── computer.gif
├── 1 - Classes
└── Classes.ipynb
├── 1 - Conditions and loops
└── Conditions_and_loops.ipynb
├── 1 - Consumer problem
└── Consumer problem.ipynb
├── 1 - Containers
└── 1 - Containers.ipynb
├── 1 - Debugging
├── Debugging.ipynb
└── mymodule.py
├── 1 - Floating point numbers
└── 1 - Floating point numbers.ipynb
├── 1 - Functions
└── Functions.ipynb
├── 1 - Git
└── Git.ipynb
├── 1 - Labor supply
├── Labor supply.ipynb
└── LaborSupplyModel.py
├── 1 - Numpy basics
└── Numpy_basics.ipynb
├── 1 - Optimize
├── Optimize.ipynb
├── consumer_module.py
└── grid_solve.py
├── 1 - Plot
├── Plot.ipynb
├── someplot.pdf
├── someplot_wireframe.pdf
└── someplot_wireframe.png
├── 1 - Print
├── Print.ipynb
├── somefile.txt
└── table1.tex
├── 1 - Production economy
├── Production economy.ipynb
└── ProductionEconomy.py
├── 1 - Random numbers advanced (+)
└── Random_numbers_advanced (+).ipynb
├── 1 - Random numbers basics
├── Random_numbers_basics.ipynb
├── data.npz
└── data.p
├── 1 - Random numbers example
├── Random_numbers_example.ipynb
└── market_eq.py
├── 1 - Structure
├── MyModel.py
├── Structure.ipynb
├── mymodule.py
├── results
│ └── xy.txt
├── simulate.py
└── solve.py
├── 2 - Combing datasets
├── Combining_datasets.ipynb
└── data
│ ├── INDKP107_long.csv
│ ├── RAS200_long.csv
│ └── area.csv
├── 2 - Data Load, clean, and save
├── Data_load_clean_and_save.ipynb
└── data
│ ├── ARE207.xlsx
│ ├── INDKP107.xlsx
│ ├── INDKP107_long.csv
│ ├── RAS200.xlsx
│ ├── RAS200_long.csv
│ └── area.csv
├── 2 - Data basics
└── Data_basics.ipynb
├── 2 - Data project lecture
├── Copilot.ipynb
├── Lecture.ipynb
└── copilot.py
├── 2 - Fetching data
└── Fetching_data.ipynb
├── 2 - Split-apply-combine
├── Split_apply_combine.ipynb
└── data
│ └── RAS200_long.csv
├── 3 - Algorithms basics
├── Algorithms_basics.ipynb
├── O-notation in math.pdf
├── ThirdGradeMultiplication.jpg
└── bigO.png
├── 3 - Constrained optimization
└── Constrained optimization.ipynb
├── 3 - Dynamic optimization
└── dynamic optimization.ipynb
├── 3 - Linear equation systems
├── Linear equation systems.ipynb
└── local_linalg.py
├── 3 - Non-linear equations
└── non-linear equations.ipynb
├── 3 - Searching and recursion
└── Searching_and_recursion.ipynb
├── 3 - Sorting
├── Sorting.ipynb
├── bubblesort.png
├── insertionsort.png
└── quicksort.png
├── 3 - Symbolic math
└── Symbolic math.ipynb
├── 3 - Unconstrained optimization
└── Unconstrained optimization.ipynb
├── 4 - Model project lecture
├── Conflictmodel.py
├── Conflictmodel_sol.py
├── ExchangeEconomy.py
├── ExchangeEconomy_sol.py
├── Lecture.ipynb
├── Lecture_sol.ipynb
└── ModelTemplate.py
├── 4 - Numba
└── numba.ipynb
├── 4 - OLG
├── OLG.ipynb
└── OLGModel.py
├── 4 - Parallization
└── parallization.ipynb
├── 4 - Ramsey
├── Ramsey.ipynb
└── RamseyModel.py
├── 4 - Speed
├── Speed.ipynb
├── computer.gif
├── memory.gif
├── moores_law.png
├── mydask.png
├── needforspeed.py
└── numpy_memory_layout.png
├── 4 - Structural estimation
├── ConsumptionSavingModel.py
└── Structural estimation.ipynb
├── 5 - Outroduction
├── Outroduction.pdf
├── Outroduction.tex
├── PS6
│ ├── numecon_linalg.py
│ └── problem_set_6.ipynb
├── PS7
│ ├── ConsumerModel.py
│ ├── ConsumerModel2.py
│ ├── consumper_
│ ├── contourplot.png
│ ├── problem_set_7.ipynb
│ └── surfaceplot.png
├── Problem_1.py
├── Problem_1_sol.py
├── Problem_1_video.py
├── Problem_2_sol.py
├── exam_2023.ipynb
├── exam_2023_sol.ipynb
├── exam_2023_video.ipynb
└── figs
│ └── ku_logo.png
├── LICENSE
├── README.md
├── admin
├── 1. GroupsOnGithub.ipynb
├── 2. PeerFeedbackAssignments.ipynb
├── 3. RepoVisibility.ipynb
├── ReadMe.md
└── groups_utils.py
├── exams
├── 2019
│ ├── ASAD.py
│ ├── exam_2019.ipynb
│ └── solution_2019.ipynb
├── 2020
│ ├── exam_2020.ipynb
│ ├── re_exam_2020.ipynb
│ ├── re_solution_2020.ipynb
│ └── solution_2020.ipynb
├── 2021
│ ├── ConsumptionSavingReexam.py
│ ├── country_region.xlsx
│ ├── exam_2021.ipynb
│ ├── poly_algo_fig.png
│ ├── re_exam_2021.ipynb
│ ├── re_solution_2021.ipynb
│ └── solution_2021.ipynb
├── 2022
│ ├── exam_2022.ipynb
│ ├── question2.py
│ ├── question3.py
│ ├── re_exam_2022.ipynb
│ ├── re_solution_2022
│ │ ├── question1.py
│ │ ├── question2.py
│ │ ├── question3.py
│ │ └── re_solution_2022.ipynb
│ ├── solution_2022.ipynb
│ └── windmills.xlsx
├── 2023
│ ├── exam_2023.ipynb
│ ├── re_exam_2023.ipynb
│ ├── re_solution_2023
│ │ ├── Problem_1.py
│ │ ├── Problem_2.py
│ │ └── re_solution_2023.ipynb
│ └── solution_2023
│ │ ├── Problem_1.py
│ │ ├── Problem_2.py
│ │ ├── Problem_3.py
│ │ └── solution_2023.ipynb
└── 2024
│ ├── exam_2024.ipynb
│ ├── re_exam_2024.ipynb
│ ├── re_solution_2024
│ ├── Problem_1.py
│ ├── Problem_2.py
│ ├── Problem_3.py
│ └── re_exam_2024.ipynb
│ └── solution_2024
│ ├── Problem_1.py
│ ├── Problem_2.py
│ └── solution_2024.ipynb
└── projects
├── 2020
├── InauguralProject2020.ipynb
├── InauguralProject2020.lyx
└── InauguralProject2020.pdf
├── 2021
├── InauguralProject2021.ipynb
├── InauguralProject2021.lyx
├── InauguralProject2021.pdf
└── InauguralProject2021.py
├── 2022
├── InauguralProject2022.ipynb
├── InauguralProject2022.lyx
├── InauguralProject2022.pdf
└── InauguralProject2022.py
├── 2023
├── HouseholdSpecializationModel.py
├── HouseholdSpecializationModel_sol.py
├── InauguralProject2023.ipynb
├── InauguralProject2023.lyx
└── InauguralProject2023.pdf
├── DataProject.lyx
├── DataProject.pdf
├── ExamProject.lyx
├── ExamProject.pdf
├── ExchangeEconomy.py
├── InauguralProject2024.ipynb
├── InauguralProject2024.lyx
├── InauguralProject2024.pdf
├── ModelProject.lyx
├── ModelProject.pdf
├── PeerFeedbackGuide.lyx
└── PeerFeedbackGuide.pdf
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | pip-wheel-metadata/
24 | share/python-wheels/
25 | *.egg-info/
26 | .installed.cfg
27 | *.egg
28 | MANIFEST
29 |
30 | # PyInstaller
31 | # Usually these files are written by a python script from a template
32 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
33 | *.manifest
34 | *.spec
35 |
36 | # Installer logs
37 | pip-log.txt
38 | pip-delete-this-directory.txt
39 |
40 | # Unit test / coverage reports
41 | htmlcov/
42 | .tox/
43 | .nox/
44 | .coverage
45 | .coverage.*
46 | .cache
47 | nosetests.xml
48 | coverage.xml
49 | *.cover
50 | *.py,cover
51 | .hypothesis/
52 | .pytest_cache/
53 |
54 | # Translations
55 | *.mo
56 | *.pot
57 |
58 | # Django stuff:
59 | *.log
60 | local_settings.py
61 | db.sqlite3
62 | db.sqlite3-journal
63 |
64 | # Flask stuff:
65 | instance/
66 | .webassets-cache
67 |
68 | # Scrapy stuff:
69 | .scrapy
70 |
71 | # Sphinx documentation
72 | docs/_build/
73 |
74 | # PyBuilder
75 | target/
76 |
77 | # Jupyter Notebook
78 | .ipynb_checkpoints
79 |
80 | # IPython
81 | profile_default/
82 | ipython_config.py
83 |
84 | # pyenv
85 | .python-version
86 |
87 | # pipenv
88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
91 | # install all needed dependencies.
92 | #Pipfile.lock
93 |
94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow
95 | __pypackages__/
96 |
97 | # Celery stuff
98 | celerybeat-schedule
99 | celerybeat.pid
100 |
101 | # SageMath parsed files
102 | *.sage.py
103 |
104 | # Environments
105 | .env
106 | .venv
107 | env/
108 | venv/
109 | ENV/
110 | env.bak/
111 | venv.bak/
112 |
113 | # Spyder project settings
114 | .spyderproject
115 | .spyproject
116 |
117 | # Rope project settings
118 | .ropeproject
119 |
120 | # mkdocs documentation
121 | /site
122 |
123 | # mypy
124 | .mypy_cache/
125 | .dmypy.json
126 | dmypy.json
127 |
128 | # Pyre type checker
129 | .pyre/
130 |
131 | # lyx
132 | *.lyx~
133 |
134 | # vscode
135 | .vscode
--------------------------------------------------------------------------------
/0 - Introduction/Introduction.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NumEconCopenhagen/IntroProg-lectures/bfd33808dbfc7bf3e60851b608c50d1bb0fbb5c2/0 - Introduction/Introduction.pdf
--------------------------------------------------------------------------------
/0 - Introduction/figs/CEBI.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NumEconCopenhagen/IntroProg-lectures/bfd33808dbfc7bf3e60851b608c50d1bb0fbb5c2/0 - Introduction/figs/CEBI.png
--------------------------------------------------------------------------------
/0 - Introduction/figs/KUSAMFLogo.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NumEconCopenhagen/IntroProg-lectures/bfd33808dbfc7bf3e60851b608c50d1bb0fbb5c2/0 - Introduction/figs/KUSAMFLogo.pdf
--------------------------------------------------------------------------------
/0 - Introduction/figs/KUSAMFtitlelrcorner.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NumEconCopenhagen/IntroProg-lectures/bfd33808dbfc7bf3e60851b608c50d1bb0fbb5c2/0 - Introduction/figs/KUSAMFtitlelrcorner.pdf
--------------------------------------------------------------------------------
/0 - Introduction/figs/ku_logo_dk_hh.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NumEconCopenhagen/IntroProg-lectures/bfd33808dbfc7bf3e60851b608c50d1bb0fbb5c2/0 - Introduction/figs/ku_logo_dk_hh.png
--------------------------------------------------------------------------------
/0 - Introduction/figs/ku_logo_uk_v.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NumEconCopenhagen/IntroProg-lectures/bfd33808dbfc7bf3e60851b608c50d1bb0fbb5c2/0 - Introduction/figs/ku_logo_uk_v.png
--------------------------------------------------------------------------------
/0 - Introduction/mymodule.py:
--------------------------------------------------------------------------------
1 | def myfunction(x):
2 | return x**2
--------------------------------------------------------------------------------
/1 - ASAD/ASAD.py:
--------------------------------------------------------------------------------
1 |
2 | from types import SimpleNamespace
3 |
4 | import numpy as np
5 |
6 | class ASADClass:
7 |
8 | def __init__(self):
9 | """ initialize the model """
10 |
11 | par = self.par = SimpleNamespace() # parameters
12 | sim = self.sim = SimpleNamespace() # simulation variables
13 | datamoms = self.datamoms = SimpleNamespace() # moments in the data
14 | moms = self.moms = SimpleNamespace() # moments in the model
15 |
16 | # a. externally given parameters
17 | par.alpha = 0.700 # slope of AD
18 | par.gamma = 0.075 # slope of SRAS
19 | par.phi = 0.99 # stickiness in expectations
20 |
21 | # b. parameters to be chosen (here guesses)
22 | par.delta = 0.80 # AR(1) of demand shock
23 | par.omega = 0.15 # AR(1) of supply shock
24 | par.sigma_x = 1.0 # std. of demand shock
25 | par.sigma_c = 0.2 # st.d of supply shock
26 |
27 | # c. misc paramters
28 | par.simT = 10_000 # length of simulation
29 |
30 | # d. calculate compound paramters
31 | self.calc_compound_par()
32 |
33 | # e. simulation
34 | sim.y_hat = np.zeros(par.simT)
35 | sim.pi_hat = np.zeros(par.simT)
36 | sim.z = np.zeros(par.simT)
37 | sim.x = np.zeros(par.simT)
38 | sim.s = np.zeros(par.simT)
39 | sim.c = np.zeros(par.simT)
40 |
41 | # f. data (numbers given in notebook)
42 | datamoms.std_y = 1.64
43 | datamoms.std_pi = 0.21
44 | datamoms.corr_y_pi = 0.31
45 | datamoms.autocorr_y = 0.84
46 | datamoms.autocorr_pi = 0.48
47 |
48 | def calc_compound_par(self):
49 | """ calculates compound parameters """
50 |
51 | par = self.par
52 |
53 | par.a = (1+par.alpha*par.gamma*par.phi)/(1+par.alpha*par.gamma)
54 | par.beta = 1/(1+par.alpha*par.gamma)
55 |
56 | def simulate(self):
57 | """ simulate the full model """
58 |
59 | np.random.seed(1917)
60 |
61 | par = self.par
62 | sim = self.sim
63 |
64 | # a. draw random shock innovations
65 | sim.x = np.random.normal(loc=0.0,scale=par.sigma_x,size=par.simT)
66 | sim.c = np.random.normal(loc=0.0,scale=par.sigma_c,size=par.simT)
67 |
68 | # b. period-by-period
69 | for t in range(par.simT):
70 |
71 | # i. lagged
72 | if t == 0:
73 | z_lag = 0.0
74 | s_lag = 0.0
75 | y_hat_lag = 0.0
76 | pi_hat_lag = 0.0
77 | else:
78 | z_lag = sim.z[t-1]
79 | s_lag = sim.s[t-1]
80 | y_hat_lag = sim.y_hat[t-1]
81 | pi_hat_lag = sim.pi_hat[t-1]
82 |
83 | # ii. AR(1) shocks
84 | z = sim.z[t] = par.delta*z_lag + sim.x[t]
85 | s = sim.s[t] = par.omega*s_lag + sim.c[t]
86 |
87 | # iii. output and inflation
88 | sim.y_hat[t] = par.a*y_hat_lag + par.beta*(z-z_lag) \
89 | - par.alpha*par.beta*s + par.alpha*par.beta*par.phi*s_lag
90 | sim.pi_hat[t] = par.a*pi_hat_lag + par.gamma*par.beta*z \
91 | - par.gamma*par.beta*par.phi*z_lag + par.beta*s - par.beta*par.phi*s_lag
92 |
93 | def calc_moms(self):
94 | """ calculate moments """
95 |
96 | # note: same moments as in the data
97 |
98 | sim = self.sim
99 | moms = self.moms
100 |
101 | moms.std_y = np.std(sim.y_hat)
102 | moms.std_pi = np.std(sim.pi_hat)
103 | moms.corr_y_pi = np.corrcoef(sim.y_hat,sim.pi_hat)[0,1]
104 | moms.autocorr_y = np.corrcoef(sim.y_hat[1:],sim.y_hat[:-1])[0,1]
105 | moms.autocorr_pi = np.corrcoef(sim.pi_hat[1:],sim.pi_hat[:-1])[0,1]
106 |
107 | def calc_diff_to_data(self,do_print=False):
108 | """ calculate difference to data """
109 |
110 | moms = self.moms
111 | datamoms = self.datamoms
112 |
113 | error = 0.0 # sum of squared differences
114 | for k in self.datamoms.__dict__.keys():
115 |
116 | diff = datamoms.__dict__[k]-moms.__dict__[k]
117 | error += diff**2
118 |
119 | if do_print: print(f'{k:12s}| data = {datamoms.__dict__[k]:.4f}, model = {moms.__dict__[k]:.4f}')
120 |
121 | if do_print: print(f'{error = :12.8f}')
122 |
123 | return error
--------------------------------------------------------------------------------
/1 - Atomic types/computer.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NumEconCopenhagen/IntroProg-lectures/bfd33808dbfc7bf3e60851b608c50d1bb0fbb5c2/1 - Atomic types/computer.gif
--------------------------------------------------------------------------------
/1 - Debugging/mymodule.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 |
3 | def g(x):
4 | y = x-5
5 | z = x
6 | return np.log(y*z)
--------------------------------------------------------------------------------
/1 - Git/Git.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# Git"
8 | ]
9 | },
10 | {
11 | "cell_type": "markdown",
12 | "metadata": {},
13 | "source": [
14 | "**Table of contents** \n",
15 | "- 1. [Central Git terms and commands](#toc1_) \n",
16 | " - 1.1. [Terms](#toc1_1_) \n",
17 | " - 1.2. [Commands](#toc1_2_) \n",
18 | "- 2. [Practice](#toc2_) \n",
19 | "- 3. [Merge conflicts](#toc3_) \n",
20 | "- 4. [More](#toc4_) \n",
21 | "\n",
22 | "\n",
29 | ""
30 | ]
31 | },
32 | {
33 | "attachments": {},
34 | "cell_type": "markdown",
35 | "metadata": {},
36 | "source": [
37 | "You will learn to use ``Git`` in ``VSCode``."
38 | ]
39 | },
40 | {
41 | "attachments": {},
42 | "cell_type": "markdown",
43 | "metadata": {},
44 | "source": [
45 | "## 1. [Git concepts and commands](#toc0_)"
46 | ]
47 | },
48 | {
49 | "attachments": {},
50 | "cell_type": "markdown",
51 | "metadata": {},
52 | "source": [
53 | "### 1.1. [Concepts](#toc0_)\n",
54 | "\n",
55 | "* the **local repository** is a folder on your own computer.\n",
56 | "* the **remote repository** is your Github repository.\n",
57 | "* a **branch:** is a separate track (or copy) of the repository.\n",
58 | "* **.gitignore** a file with rules for files to ignore.\n",
59 | "* **.git** hidden folder with diff and head files containing history of changes.\n",
60 | "\n",
61 | "You will only work with a **main** branch. \n",
62 | "\n",
63 | "* Additional branches can be used to develop new stuff \n",
64 | "* ... and then later merge them on the main branch.\n",
65 | "\n",
66 | "You won't need to edit *.gitignore* or look in the *.git* folder."
67 | ]
68 | },
69 | {
70 | "attachments": {},
71 | "cell_type": "markdown",
72 | "metadata": {},
73 | "source": [
74 | "### 1.2. [Commands](#toc0_)\n",
75 | "\n",
76 | "**Downloading:**\n",
77 | "\n",
78 | "1. **Fetch:** Get list of remotely added or changed files.\n",
79 | "2. **Merge:** Merge in all remote changes to your local repository.\n",
80 | "3. **Pull:** Combination of fetching and merging.\n",
81 | "\n",
82 | "**Uploading:**\n",
83 | "\n",
84 | "1. **Stage:** Set list of files you have added or changed .\n",
85 | "2. **Commit:** Label the staged package.\n",
86 | "3. **Push:** Apply the changes to the remote repository (assuming you are *admin*).\n",
87 | "\n",
88 | "**In VS Code** you only need to use two commands:\n",
89 | "\n",
90 | "* **Commit all:** *Stage* and *commit*. \n",
91 | "* **Sync:** First *pull* and then *push*."
92 | ]
93 | },
94 | {
95 | "cell_type": "markdown",
96 | "metadata": {},
97 | "source": [
98 | "## 2. [Practice](#toc0_)"
99 | ]
100 | },
101 | {
102 | "attachments": {},
103 | "cell_type": "markdown",
104 | "metadata": {},
105 | "source": [
106 | "**To get started:** Set up your own repository following [Make your own Github classroom repository guide](https://sites.google.com/view/numeconcph-introprog/guides/git)\n",
107 | "\n",
108 | "My example repository is https://github.com/NumEconCopenhagen/projects-2023-test\n",
109 | "\n"
110 | ]
111 | },
112 | {
113 | "attachments": {},
114 | "cell_type": "markdown",
115 | "metadata": {},
116 | "source": [
117 | "**Task:** Make, view and sync changes.\n",
118 | "\n",
119 | "1. Open ``README.md`` in your repository.\n",
120 | "2. Change some lines of text and delete some others.\n",
121 | "3. Click on the symbols next to line number to see what have changed.\n",
122 | "4. Press ``Alt+F3`` to go to next change.\n",
123 | "5. Open ``Source Control`` (e.g. by ``Ctrl+Shift+G``).\n",
124 | "6. Click on ``README.MD`` to see ``Working Tree`` with changes, and close again.\n",
125 | "7. Note:\n",
126 | "\n",
127 | " A ``M`` implies the file is *Modfieid*.\n",
128 | "\n",
129 | " A ``U`` implied the file is *Untracked* (i.e. new).\n",
130 | "\n",
131 | " A ``D`` implied the file is *Deleted*.\n",
132 | "\n",
133 | " You can ``Discard Changes`` by clicking backward bending arrow. \n",
134 | " \n",
135 | "\n",
136 | "7. Write ``Message`` and click ``Commit``.\n",
137 | "8. Click ``Sync``."
138 | ]
139 | },
140 | {
141 | "attachments": {},
142 | "cell_type": "markdown",
143 | "metadata": {},
144 | "source": [
145 | "**Observation:** Even working on you own Git can help you get an overview of what you are chaing.\n",
146 | "\n",
147 | "**Note:** Git can be used from the command control palette, ``Ctrl+Shift+P``."
148 | ]
149 | },
150 | {
151 | "cell_type": "markdown",
152 | "metadata": {},
153 | "source": [
154 | "## 3. [Merge conflicts](#toc0_)\n",
155 | "\n",
156 | "**Problem:** You are changing code-lines, which have already been changed remotely!\n",
157 | "\n",
158 | "**Avoid:** Run ``Sync`` each time before you start working.\n",
159 | "\n",
160 | "**Resolve:** Use point-and-click in ``VSCode``.\n",
161 | "\n",
162 | "**Brute-force:** \n",
163 | "\n",
164 | "1. Copy-out content of current local repository and delete folder.\n",
165 | "2. ``Clone`` the remote repository again (*with the conflicting added and updated files*).\n",
166 | "3. Copy-in new files one-by-one and evaluate changes."
167 | ]
168 | },
169 | {
170 | "attachments": {},
171 | "cell_type": "markdown",
172 | "metadata": {},
173 | "source": [
174 | "## 4. [More on Git](#toc0_)"
175 | ]
176 | },
177 | {
178 | "attachments": {},
179 | "cell_type": "markdown",
180 | "metadata": {},
181 | "source": [
182 | "1. Follow [Introduction to Git](https://www.datacamp.com/courses/introduction-to-git) at DataCamp\n",
183 | "2. See video on [Using Git with Visual Studio Code (Official Tutorial)](https://www.youtube.com/watch?v=i_23KUAEtUM)"
184 | ]
185 | }
186 | ],
187 | "metadata": {
188 | "kernelspec": {
189 | "display_name": "base",
190 | "language": "python",
191 | "name": "python3"
192 | },
193 | "language_info": {
194 | "codemirror_mode": {
195 | "name": "ipython",
196 | "version": 3
197 | },
198 | "file_extension": ".py",
199 | "mimetype": "text/x-python",
200 | "name": "python",
201 | "nbconvert_exporter": "python",
202 | "pygments_lexer": "ipython3",
203 | "version": "3.9.15 (main, Nov 24 2022, 14:39:17) [MSC v.1916 64 bit (AMD64)]"
204 | },
205 | "toc-autonumbering": true,
206 | "vscode": {
207 | "interpreter": {
208 | "hash": "47ef90cdf3004d3f859f1fb202523c65c07ba7c22eefd261b181f4744e2d0403"
209 | }
210 | }
211 | },
212 | "nbformat": 4,
213 | "nbformat_minor": 4
214 | }
215 |
--------------------------------------------------------------------------------
/1 - Labor supply/LaborSupplyModel.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | from scipy import optimize
3 |
4 | def implied_tax(l,w,tau0,tau1,kappa):
5 | """ calculate implied tax of labor supply choice
6 |
7 | Args:
8 |
9 | l (float): labor supply
10 | w (float): wage
11 | tau0 (float): standard labor tax
12 | tau1 (float): top bracket labor income tax
13 | kappa (float): cut-off for the top labor income bracket
14 |
15 | Returns:
16 |
17 | (float): total tax bill
18 |
19 | """
20 |
21 | return tau0*w*l + tau1*np.fmax(w*l-kappa,0)
22 |
23 | def implied_c(l,m,w,tau0,tau1,kappa):
24 | """ calculate implied optimal consumption of labor supply choice
25 |
26 | Args:
27 |
28 | l (float): labor supply
29 | m (float): cash-on-hand
30 | w (float): wage
31 | tau0 (float): standard labor tax
32 | tau1 (float): top bracket labor income tax
33 | kappa (float): cut-off for the top labor income bracket
34 |
35 | Returns:
36 |
37 | (float): consumption
38 |
39 | """
40 |
41 | return m + w*l - implied_tax(l,w,tau0,tau1,kappa)
42 |
43 | def utility(c,l,nu,frisch):
44 | """ utility of consumption and labor supply decision
45 |
46 | Args:
47 |
48 | c (float): consumption
49 | l (float): labor supply
50 | nu (float): disutility of labor supply
51 | frisch (float): frisch elasticity of labor supply
52 |
53 | Returns:
54 |
55 | (float): utility
56 |
57 | """
58 |
59 | return np.log(c) - nu*l**(1+1/frisch)/(1+1/frisch)
60 |
61 | def value_of_choice(l,nu,frisch,m,w,tau0,tau1,kappa):
62 | """ calculate implied utlity of consumption and labor supply choice
63 |
64 | Args:
65 |
66 | l (float): labor supply
67 | nu (float): disutility of labor supply
68 | frisch (float): frisch elasticity of labor supply
69 | m (float): cash-on-hand
70 | w (float): wage
71 | tau0 (float): standard labor tax
72 | tau1 (float): top bracket labor income tax
73 | kappa (float): cut-off for the top labor income bracket
74 |
75 | Returns:
76 |
77 | (float): utility
78 |
79 | """
80 |
81 | c = implied_c(l,m,w,tau0,tau1,kappa)
82 | return utility(c,l,nu,frisch)
83 |
84 | def find_optimal_labor_supply(nu,frisch,m,w,tau0,tau1,kappa):
85 | """ find optimal labor supply choice
86 |
87 | Args:
88 |
89 | nu (float): disutility of labor supply
90 | frisch (float): frisch elasticity of labor supply
91 | m (float): cash-on-hand
92 | w (float): wage
93 | tau0 (float): standard labor tax
94 | tau1 (float): top bracket labor income tax
95 | kappa (float): cut-off for the top labor income bracket
96 |
97 | Returns:
98 |
99 | (float): optimal labor supply
100 |
101 | """
102 |
103 | obj = lambda l: -value_of_choice(l,nu,frisch,m,w,tau0,tau1,kappa)
104 | res = optimize.minimize_scalar(obj,bounds=(0,1),method='bounded')
105 |
106 | return res.x
--------------------------------------------------------------------------------
/1 - Optimize/consumer_module.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | from scipy import optimize
3 | import matplotlib.pyplot as plt
4 |
5 | class consumer:
6 |
7 | def __init__(self,**kwargs): # called when created
8 |
9 | # a. baseline parameters
10 | self.alpha = 0.5
11 |
12 | self.p1 = 1
13 | self.p2 = 2
14 | self.I = 10
15 |
16 | self.x1 = np.nan # not-a-number
17 | self.x2 = np.nan
18 |
19 | # b. baseline settings
20 | self.x2_max = 10
21 | self.N = 100
22 |
23 | # c. update parameters and settings
24 | for key, value in kwargs.items():
25 | setattr(self,key,value) # like self.key = value
26 |
27 | # note: "kwargs" is a dictionary with keyword arguments
28 |
29 | def __str__(self): # called when printed
30 |
31 | lines = f'alpha = {self.alpha:.3f}\n'
32 | lines += f'price vector = (p1,p2) = ({self.p1:.3f},{self.p2:.3f})\n'
33 | lines += f'income = I = {self.I:.3f}\n'
34 |
35 | # add lines on solution if it has been calculated
36 | if not (np.isnan(self.x1) or np.isnan(self.x2)):
37 | lines += 'solution:\n'
38 | lines += f' x1 = {self.x1:.2f}\n'
39 | lines += f' x2 = {self.x2:.2f}\n'
40 |
41 | return lines
42 |
43 | # note: \n gives a lineshift
44 |
45 | # utilty function
46 | def u_func(self,x1,x2):
47 | return x1**self.alpha*x2**(1-self.alpha)
48 |
49 | # solve problem
50 | def solve(self):
51 |
52 | # a. objective function (to minimize)
53 | def value_of_choice(x):
54 | return -self.u_func(x[0],x[1])
55 |
56 | # b. constraints
57 | constraints = ({'type': 'ineq', 'fun': lambda x: self.I-self.p1*x[0]-self.p2*x[1]})
58 | bounds = ((0,self.I/self.p1),(0,self.I/self.p2))
59 |
60 | # c. call solver
61 | initial_guess = [self.I/self.p1/2,self.I/self.p2/2]
62 | sol = optimize.minimize(value_of_choice,initial_guess,
63 | method='SLSQP',bounds=bounds,constraints=constraints)
64 |
65 | # d. save
66 | self.x1 = sol.x[0]
67 | self.x2 = sol.x[1]
68 | self.u = self.u_func(self.x1,self.x2)
69 |
70 | # find indifference curves
71 | def find_indifference_curves(self):
72 |
73 | # allocate memory
74 | self.x1_vecs = []
75 | self.x2_vecs = []
76 | self.us = []
77 |
78 | for fac in [0.5,1,1.5]:
79 |
80 | # fac = 1 -> indifference curve through optimum
81 | # fac < 1 -> ... below optimum
82 | # fac > 1 -> ... above optimum
83 |
84 | # a. utility in (fac*x1,fac*x2)
85 | u = self.u_func(fac*self.x1,fac*self.x2)
86 |
87 | # b. allocate numpy arrays
88 | x1_vec = np.empty(self.N)
89 | x2_vec = np.linspace(1e-8,self.x2_max,self.N)
90 |
91 | # c. loop through x2 and find x1
92 | for i,x2 in enumerate(x2_vec):
93 |
94 | # local function given value of u and x2
95 | def objective(x1):
96 | return self.u_func(x1,x2)-u
97 |
98 | sol = optimize.root(objective, 0)
99 | x1_vec[i] = sol.x[0]
100 |
101 | # d. save
102 | self.x1_vecs.append(x1_vec)
103 | self.x2_vecs.append(x2_vec)
104 | self.us.append(u)
105 |
106 | # plot budgetset
107 | def plot_budgetset(self,ax):
108 |
109 | x = [0,0,self.I/self.p1] # x-cordinates in triangle
110 | y = [0,self.I/self.p2,0] # y-cordinates in triangle
111 |
112 | # fill triangle
113 | ax.fill(x,y,color="firebrick",lw=2,alpha=0.5) # alpha controls transparance
114 |
115 | # plot solution
116 | def plot_solution(self,ax):
117 |
118 | ax.plot(self.x1,self.x2,'ro') # a black dot
119 | ax.text(self.x1*1.03,self.x2*1.03,f'$u^{{max}} = {self.u:.2f}$')
120 |
121 | # plot indifference curve
122 | def plot_indifference_curves(self,ax):
123 |
124 | for x1_vec,x2_vec,u in zip(self.x1_vecs,self.x2_vecs,self.us):
125 | ax.plot(x1_vec,x2_vec,label=f'$u = {u:.2f}$')
126 |
127 | # details of the plot (label,limits,grid,legend)
128 | def plot_details(self,ax):
129 |
130 | ax.set_xlabel('$x_1$')
131 | ax.set_ylabel('$x_2$')
132 |
133 | ax.set_xlim([0,self.x2_max])
134 | ax.set_ylim([0,self.x2_max])
135 |
136 | ax.grid(ls='--',lw=1)
137 | ax.legend(loc='upper right')
138 |
139 |
140 | def plot_everything(self):
141 | fig = plt.figure(figsize=(5,5))
142 | ax = fig.add_subplot(1,1,1)
143 |
144 | self.plot_indifference_curves(ax)
145 | self.plot_budgetset(ax)
146 | self.plot_solution(ax)
147 | self.plot_details(ax)
--------------------------------------------------------------------------------
/1 - Optimize/grid_solve.py:
--------------------------------------------------------------------------------
1 | # All modules used within a module must be imported locally
2 | import numpy as np
3 |
4 | # You need to respecify the u_func, because the module does not share scope with the notebook.
5 | # That is, the module functions cannot see that u_func was defined in the notebook when find_best_choice is called
6 | def u_func(x1,x2,alpha=0.50):
7 | return x1**alpha * x2**(1-alpha)
8 |
9 | def find_best_choice(alpha,I,p1,p2,N1,N2,do_print=True):
10 |
11 | # a. allocate numpy arrays
12 | shape_tuple = (N1,N2)
13 | x1_values = np.empty(shape_tuple)
14 | x2_values = np.empty(shape_tuple)
15 | u_values = np.empty(shape_tuple)
16 |
17 | # b. start from guess of x1=x2=0
18 | x1_best = 0
19 | x2_best = 0
20 | u_best = u_func(0,0,alpha=alpha)
21 |
22 | # c. loop through all possibilities
23 | for i in range(N1):
24 | for j in range(N2):
25 |
26 | # i. x1 and x2 (chained assignment)
27 | x1_values[i,j] = x1 = (i/(N1-1))*I/p1
28 | x2_values[i,j] = x2 = (j/(N2-1))*I/p2
29 |
30 | # ii. utility
31 | if p1*x1 + p2*x2 <= I: # u(x1,x2) if expenditures <= income
32 | u_values[i,j] = u_func(x1,x2,alpha=alpha)
33 | else: # u(0,0) if expenditures > income, not allowed
34 | u_values[i,j] = u_func(0,0,alpha=alpha)
35 |
36 | # iii. check if best sofar
37 | if u_values[i,j] > u_best:
38 | x1_best = x1_values[i,j]
39 | x2_best = x2_values[i,j]
40 | u_best = u_values[i,j]
41 |
42 | # d. print
43 | if do_print:
44 | print_solution(x1_best,x2_best,u_best,I,p1,p2)
45 |
46 | return x1_best,x2_best,u_best,x1_values,x2_values,u_values
47 |
48 | # function for printing the solution
49 | def print_solution(x1,x2,u,I,p1,p2):
50 | print(f'x1 = {x1:.4f}')
51 | print(f'x2 = {x2:.4f}')
52 | print(f'u = {u:.4f}')
53 | print(f'I-p1*x1-p2*x2 = {I-p1*x1-p2*x2:.8f}')
54 | print(f'x1*p1/I = {x1*p1/I:.4f}')
55 |
56 |
57 | def find_best_choice_monotone(alpha,I,p1,p2,N,do_print=True):
58 |
59 | # a. allocate numpy arrays
60 | shape_tuple = (N)
61 | x1_values = np.empty(shape_tuple)
62 | x2_values = np.empty(shape_tuple)
63 | u_values = np.empty(shape_tuple)
64 |
65 | # b. start from guess of x1=x2=0
66 | x1_best = 0
67 | x2_best = 0
68 | u_best = u_func(0,0,alpha)
69 |
70 | # c. loop through all possibilities
71 | for i in range(N):
72 |
73 | # i. x1
74 | x1_values[i] = x1 = i/(N-1)*I/p1
75 |
76 | # ii. implied x2 by budget constraint
77 | x2_values[i] = x2 = (I-p1*x1)/p2
78 |
79 | # iii. utility
80 | u_values[i] = u_func(x1,x2,alpha)
81 |
82 | if u_values[i] >= u_best:
83 | x1_best = x1_values[i]
84 | x2_best = x2_values[i]
85 | u_best = u_values[i]
86 |
87 | # d. print
88 | if do_print:
89 | print_solution(x1_best,x2_best,u_best,I,p1,p2)
90 |
91 | return x1_best,x2_best,u_best,x1_values,x2_values,u_values
--------------------------------------------------------------------------------
/1 - Plot/someplot.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NumEconCopenhagen/IntroProg-lectures/bfd33808dbfc7bf3e60851b608c50d1bb0fbb5c2/1 - Plot/someplot.pdf
--------------------------------------------------------------------------------
/1 - Plot/someplot_wireframe.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NumEconCopenhagen/IntroProg-lectures/bfd33808dbfc7bf3e60851b608c50d1bb0fbb5c2/1 - Plot/someplot_wireframe.pdf
--------------------------------------------------------------------------------
/1 - Plot/someplot_wireframe.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NumEconCopenhagen/IntroProg-lectures/bfd33808dbfc7bf3e60851b608c50d1bb0fbb5c2/1 - Plot/someplot_wireframe.png
--------------------------------------------------------------------------------
/1 - Print/somefile.txt:
--------------------------------------------------------------------------------
1 | 10: x1 = 2.000 x2 = 3.000 -> u = 2.711
2 | 11: x1 = 4.000 x2 = 3.000 -> u = 3.224
3 | 12: x1 = 6.000 x2 = 3.000 -> u = 3.568
4 | 13: x1 = 8.000 x2 = 3.000 -> u = 3.834
5 |
--------------------------------------------------------------------------------
/1 - Print/table1.tex:
--------------------------------------------------------------------------------
1 | \begin{tabular}{l|ccc}
2 | \toprule
3 | & $x_{1}$ & $x_{2}$ & $u\left(x_{1},x_{2}\right)$ \\
4 | \midrule
5 | a & 2 & 3 & 2.711 utils \\
6 | b & 4 & 3 & 3.224 utils \\
7 | c & 6 & 3 & 3.568 utils \\
8 | d & 8 & 3 & 3.834 utils \\
9 | \bottomrule
10 | \end{tabular}
11 |
--------------------------------------------------------------------------------
/1 - Production economy/ProductionEconomy.py:
--------------------------------------------------------------------------------
1 | from types import SimpleNamespace
2 | import numpy as np
3 | from scipy import optimize
4 |
5 | class ProductionEconomyClass():
6 |
7 | def __init__(self):
8 |
9 | par = self.par = SimpleNamespace()
10 |
11 | # a. parameters
12 | par.kappa = 0.1 # home production
13 | par.omega = 10 # disutility of labor supply factor
14 | par.eta = 1.50 # curvature of disutility of labor supply
15 | par.alpha = 0.50 # curvature of production function
16 | par.Nw = 99 # number of workers
17 | par.Nc = 1 # number of capitalists
18 |
19 | # b. grids
20 | par.num_w = 10
21 | par.grid_w = np.linspace(0.1,1.5,par.num_w)
22 | par.grid_mkt_clearing = np.zeros(par.num_w)
23 |
24 | # c. solution
25 | sol = self.sol = SimpleNamespace()
26 |
27 | sol.p = 1 # output price
28 | sol.w = 1 # wage
29 |
30 | def utility_w(self,c,l):
31 | """ utility of workers """
32 |
33 | par = self.par
34 |
35 | return np.log(c+par.kappa)-par.omega*l**par.eta
36 |
37 | def workers(self):
38 | """ maximize utility for workers """
39 |
40 | sol = self.sol
41 |
42 | p = sol.p
43 | w = sol.w
44 |
45 | # a. solve
46 | obj = lambda l: -self.utility_w((w*l)/p,l) # substitute in the budget constraint
47 | res = optimize.minimize_scalar(obj,bounds=(0,1),method='bounded')
48 |
49 | # b. save
50 | sol.l_w_star = res.x
51 | sol.c_w_star = (w*sol.l_w_star)/p
52 |
53 | def utility_c(self,c,l):
54 | """ utility of capitalists """
55 |
56 | par = self.par
57 |
58 | return np.log(c+par.kappa)-par.omega*l**par.eta
59 |
60 | def capitalists(self):
61 | """ maximize utility of capitalists """
62 |
63 | sol = self.sol
64 |
65 | p = sol.p
66 | w = sol.w
67 | pi = sol.pi
68 |
69 | # a. solve
70 | obj = lambda l: -self.utility_c((w*l+pi)/p,l) # subsittute in the budget constraint
71 | res = optimize.minimize_scalar(obj,bounds=(0,1),method='bounded')
72 |
73 | # b. save
74 | sol.l_c_star = res.x
75 | sol.c_c_star = (w*sol.l_c_star+pi)/p
76 |
77 | def firm(self):
78 | """ maximize firm profits """
79 |
80 | par = self.par
81 | sol = self.sol
82 |
83 | p = sol.p
84 | w = sol.w
85 |
86 | # a. solve
87 | f = lambda l: l**par.alpha
88 | obj = lambda l: -(p*f(l)-w*l)
89 | x0 = [0.0]
90 | res = optimize.minimize(obj,x0,bounds=((0,None),),method='L-BFGS-B')
91 |
92 | # b. save
93 | sol.l_star = res.x[0]
94 | sol.y_star = f(sol.l_star)
95 | sol.Pi = p*sol.y_star-w*sol.l_star
96 |
97 | def evaluate_equilibrium(self):
98 | """ evaluate equilirium """
99 |
100 | par = self.par
101 | sol = self.sol
102 |
103 | # a. optimal behavior of firm
104 | self.firm()
105 | sol.pi = sol.Pi/par.Nc
106 |
107 | # b. optimal behavior of households
108 | self.workers()
109 | self.capitalists()
110 |
111 | # c. market clearing
112 | sol.goods_mkt_clearing = par.Nw*sol.c_w_star + par.Nc*sol.c_c_star - sol.y_star
113 | sol.labor_mkt_clearing = par.Nw*sol.l_w_star + par.Nc*sol.l_c_star - sol.l_star
114 |
115 | def find_equilibrium(self):
116 |
117 | par = self.par
118 | sol = self.sol
119 |
120 | # a. grid search
121 | print('grid search:')
122 | for i,w in enumerate(par.grid_w):
123 | sol.w = w
124 | self.evaluate_equilibrium()
125 | par.grid_mkt_clearing[i] = sol.goods_mkt_clearing
126 | print(f' w = {w:.2f} -> {par.grid_mkt_clearing[i]:12.8f}')
127 |
128 | print('')
129 |
130 | # b. find bounds
131 | left = np.max(par.grid_w[par.grid_mkt_clearing < 0])
132 | right = np.min(par.grid_w[par.grid_mkt_clearing > 0])
133 | print(f'equilibrium price must be in [{left:.2f},{right:.2f}]\n')
134 |
135 | # c. bisection search
136 | def obj(w):
137 | sol.w = w
138 | self.evaluate_equilibrium()
139 | return sol.goods_mkt_clearing
140 |
141 | res = optimize.root_scalar(obj,bracket=[left,right],method='bisect')
142 | sol.w = res.root
143 | print(f'the equilibrium wage is {sol.w:.4f}\n')
144 |
145 | # d. show result
146 | u_w = self.utility_w(sol.c_w_star,sol.l_w_star)
147 | print(f'workers : c = {sol.c_w_star:6.4f}, l = {sol.l_w_star:6.4f}, u = {u_w:7.4f}')
148 | u_c = self.utility_c(sol.c_c_star,sol.l_c_star)
149 | print(f'capitalists : c = {sol.c_c_star:6.4f}, l = {sol.l_c_star:6.4f}, u = {u_c:7.4f}')
150 | print(f'goods market : {sol.goods_mkt_clearing:.8f}')
151 | print(f'labor market : {sol.labor_mkt_clearing:.8f}')
--------------------------------------------------------------------------------
/1 - Random numbers basics/data.npz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NumEconCopenhagen/IntroProg-lectures/bfd33808dbfc7bf3e60851b608c50d1bb0fbb5c2/1 - Random numbers basics/data.npz
--------------------------------------------------------------------------------
/1 - Random numbers basics/data.p:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NumEconCopenhagen/IntroProg-lectures/bfd33808dbfc7bf3e60851b608c50d1bb0fbb5c2/1 - Random numbers basics/data.p
--------------------------------------------------------------------------------
/1 - Random numbers example/market_eq.py:
--------------------------------------------------------------------------------
1 | # All modules used within a module must be imported locally
2 | import numpy as np
3 |
4 | class MarketEq():
5 |
6 | def __init__(self,**kwargs):
7 | '''
8 | Initialize the model with parameters
9 | '''
10 |
11 | self.N = 1000 # number of agents
12 | self.k = 2 # relative endowment of good 1
13 | self.mu_low = 0.1 # lower bound on alpha
14 | self.mu_high = 0.9 # upper bound on alpha
15 | self.kappa = 0.1 # Adjustment factor for solving
16 | self.eps = 1e-8 # Tolerance parameter for solving
17 | self.maxiter=500 # Max iterations when solving
18 |
19 |
20 | # Update values according to kwargs
21 | for key, value in kwargs.items():
22 | setattr(self,key,value) # like self.key = value
23 |
24 | # Simulate alphas according to parameters
25 | self.simulate_agents()
26 |
27 | def simulate_agents(self):
28 | '''
29 | Simulate alphas for all agents
30 | '''
31 | self.alphas = np.random.uniform(low=self.mu_low, high=self.mu_high, size=self.N)
32 |
33 | def demand_good_1_func(self,p1,p2):
34 | I = self.k*p1+p2
35 | return self.alphas*I/p1
36 |
37 | def demand_good_2_func(self,p1,p2):
38 | I = self.k*p1+p2
39 | return (1-self.alphas)*I/p2
40 |
41 | def excess_demand_good_1_func(self,p1,p2):
42 |
43 | # a. demand
44 | demand = np.sum(self.demand_good_1_func(p1,p2))
45 |
46 | # b. supply
47 | supply = self.k*self.N
48 |
49 | # c. excess demand
50 | excess_demand = demand-supply
51 |
52 | return excess_demand
53 |
54 | def excess_demand_good_2_func(self,p1,p2):
55 |
56 | # a. demand
57 | demand = np.sum(self.demand_good_2_func(p1,p2))
58 |
59 | # b. supply
60 | supply = self.N
61 |
62 | # c. excess demand
63 | excess_demand = demand-supply
64 |
65 | return excess_demand
66 |
67 | def find_equilibrium(self,p1_guess,p2):
68 |
69 | t = 0
70 | p1 = p1_guess
71 |
72 | # using a while loop as we don't know number of iterations a priori
73 | while True:
74 |
75 | # a. step 1: excess demand
76 | Z1 = self.excess_demand_good_1_func(p1,p2)
77 |
78 | # b: step 2: stop?
79 | if np.abs(Z1) < self.eps or t >= self.maxiter:
80 | print(f'{t:3d}: p1 = {p1:12.8f} -> excess demand -> {Z1:14.8f}')
81 | break
82 |
83 | # c. Print the first 5 and every 25th iteration using the modulus operator
84 | if t < 5 or t%25 == 0:
85 | print(f'{t:3d}: p1 = {p1:12.8f} -> excess demand -> {Z1:14.8f}')
86 | elif t == 5:
87 | print(' ...')
88 |
89 | # d. step 3: update p1
90 | p1 = p1 + self.kappa*Z1/self.N
91 |
92 | # e. step 4: update counter and return to step 1
93 | t += 1
94 |
95 |
96 | # Check if solution is found
97 | if np.abs(Z1) < self.eps:
98 | # Store equilibrium prices
99 | self.p1_star = p1
100 | self.p2_star = p2
101 |
102 | # Store equilibrium excess demand
103 | self.Z1 = Z1
104 | self.Z2 = self.excess_demand_good_2_func(self.p1_star, self.p2_star)
105 |
106 | # Make sure that Walras' law is satisfied
107 | if not np.abs(self.Z2) self.data['Adj Close RM 50']).astype(int)
39 | self.data.loc[cross_points.diff() != 0, 'Cross'] = 1
40 |
41 | def calculate_returns(self):
42 | if self.data is None:
43 | print("No data available. Download data first.")
44 | return
45 |
46 | # Calculate cumulative returns
47 | self.data['Return'] = self.data['Adj Close'].pct_change()
48 | self.data['Cumulative Return'] = (1 + self.data['Return']).cumprod()
49 |
50 | # Calculate alternative cumulative return
51 | self.data['Position'] =(self.data['Adj Close RM 50'] > self.data['Adj Close RM 200']).astype(int)
52 | #self.data['Position'].fillna(method='ffill', inplace=True)
53 | self.data['Alternative Return'] = self.data['Return'] * self.data['Position']
54 | self.data['Alternative Cumulative Return'] = (1 + self.data['Alternative Return']).cumprod()
55 |
56 |
57 | def plot(self, start_year, end_year, variables=['Adj Close', 'Adj Close RM 200', 'Adj Close RM 50']):
58 | if self.data is None:
59 | print("No data available. Download data first.")
60 | return
61 |
62 | start_date = datetime(start_year, 1, 1)
63 | end_date = datetime(end_year, 12, 31)
64 |
65 | df = self.data.loc[start_date:end_date].copy()
66 |
67 | # Rebase the cumulative returns if they are to be plotted
68 | if 'Cumulative Return' in variables or 'Alternative Cumulative Return' in variables:
69 | df['Cumulative Return'] /= df['Cumulative Return'].iloc[0]
70 | df['Alternative Cumulative Return'] /= df['Alternative Cumulative Return'].iloc[0]
71 |
72 | fig, ax = plt.subplots(figsize=(12,8))
73 | df[variables].plot(ax=ax)
74 |
75 | # Add vertical lines at the points where the two moving averages cross
76 | cross_points = df[df['Cross'] == 1].index
77 | for cross_point in cross_points:
78 | ax.axvline(x=cross_point, color='r', linestyle='--')
79 |
80 | ax.set_title(f'{self.ticker} Plot')
81 | ax.set_xlabel('Date')
82 | ax.set_ylabel('Value')
83 | plt.show()
84 |
85 |
86 |
87 | def plot_data_(self, start_year, end_year):
88 | if self.data is None:
89 | print("No data available. Download data first.")
90 | return
91 |
92 | start_date = datetime(start_year, 1, 1)
93 | end_date = datetime(end_year, 12, 31)
94 |
95 | df = self.data.loc[start_date:end_date].copy()
96 | fig, ax = plt.subplots(figsize=(12,8))
97 | df[['Adj Close', 'Adj Close RM 200', 'Adj Close RM 50']].plot(ax=ax)
98 |
99 | # Add vertical lines at the points where the two moving averages cross
100 | cross_points = df[df['Cross'] == 1].index
101 | for cross_point in cross_points:
102 | ax.axvline(x=cross_point, color='r', linestyle='--')
103 |
104 | ax.set_title(f'{self.ticker} Adjusted Close Price and Running Means')
105 | ax.set_xlabel('Date')
106 | ax.set_ylabel('Price')
107 | plt.show()
108 |
109 |
110 |
111 | def plot_data_(self, start_year, end_year):
112 | if self.data is None:
113 | print("No data available. Download data first.")
114 | return
115 |
116 | start_date = datetime(start_year, 1, 1)
117 | end_date = datetime(end_year, 12, 31)
118 |
119 | df = self.data.loc[start_date:end_date].copy()
120 | fig, ax = plt.subplots(figsize=(12,8))
121 | df[['Adj Close', 'Adj Close RM 200', 'Adj Close RM 50']].plot(ax=ax)
122 |
123 | # Add vertical lines at the points where the two moving averages cross
124 | cross_points = df[df['Cross'] == 1].index
125 | for cross_point in cross_points:
126 | ax.axvline(x=cross_point, color='r', linestyle='--')
127 |
128 | ax.set_title(f'{self.ticker} Adjusted Close Price and Running Means')
129 | ax.set_xlabel('Date')
130 | ax.set_ylabel('Price')
131 | plt.show()
132 |
133 |
134 | def plot_returns(self, start_year, end_year):
135 | if self.data is None:
136 | print("No data available. Download data first.")
137 | return
138 |
139 | start_date = datetime(start_year, 1, 1)
140 | end_date = datetime(end_year, 12, 31)
141 |
142 | df = self.data.loc[start_date:end_date].copy()
143 | fig, ax = plt.subplots(figsize=(12,8))
144 |
145 | # Rebase the cumulative returns to the start year
146 | df['Cumulative Return'] /= df['Cumulative Return'].iloc[0]
147 | df['Alternative Cumulative Return'] /= df['Alternative Cumulative Return'].iloc[0]
148 |
149 | df[['Cumulative Return', 'Alternative Cumulative Return']].plot(ax=ax)
150 |
151 | # Add vertical lines at the points where the two moving averages cross
152 | cross_points = df[df['Cross'] == 1].index
153 | for cross_point in cross_points:
154 | ax.axvline(x=cross_point, color='r', linestyle='--')
155 |
156 | ax.set_title(f'{self.ticker} Cumulative Returns and Crossover Points')
157 | ax.set_xlabel('Date')
158 | ax.set_ylabel('Cumulative Return')
159 | plt.show()
--------------------------------------------------------------------------------
/3 - Algorithms basics/O-notation in math.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NumEconCopenhagen/IntroProg-lectures/bfd33808dbfc7bf3e60851b608c50d1bb0fbb5c2/3 - Algorithms basics/O-notation in math.pdf
--------------------------------------------------------------------------------
/3 - Algorithms basics/ThirdGradeMultiplication.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NumEconCopenhagen/IntroProg-lectures/bfd33808dbfc7bf3e60851b608c50d1bb0fbb5c2/3 - Algorithms basics/ThirdGradeMultiplication.jpg
--------------------------------------------------------------------------------
/3 - Algorithms basics/bigO.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NumEconCopenhagen/IntroProg-lectures/bfd33808dbfc7bf3e60851b608c50d1bb0fbb5c2/3 - Algorithms basics/bigO.png
--------------------------------------------------------------------------------
/3 - Linear equation systems/local_linalg.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import scipy as sp
3 |
4 | def gauss_jordan(A,no_reduction=False):
5 | """ Gauss-Jordan elimination
6 |
7 | Convert a matrix to its row (reduced) echelon form.
8 |
9 | Args:
10 |
11 | A (ndarray): input matrix to work on in-place
12 | no_reduction (bool): stop when non-reduced echelon form is reached
13 |
14 | """
15 |
16 | n,_m = A.shape
17 |
18 | # row echelon form (Gauss elimination)
19 | for i in range(0, n):
20 |
21 | # a. search for maximum in this column
22 | maxrow = i + np.argmax(abs(A[i:,i]))
23 |
24 | # b. swap maximum row with current row (column by column)
25 | temp = A[maxrow,i:].copy()
26 | A[maxrow,i:] = A[i,i:]
27 | A[i,i:] = temp
28 |
29 | # b. make all rows below this one 0 in current column
30 | for k in range(i+1, n):
31 | c = -A[k,i]/A[i,i]
32 | A[k,i] = 0
33 | A[k,i+1:] += c*A[i,i+1:]
34 |
35 | if no_reduction:
36 | return
37 |
38 | # reduced row echelon form (Gauss-Jordan elimination)
39 | for i in range(n-1,-1,-1):
40 |
41 | # a. normalize this row
42 | c = A[i,i]
43 | A[i,:] /= c
44 |
45 | # b. make all rows above this one 0 in the current column
46 | for j in range(0,i):
47 | c = A[j,i]
48 | A[j,:] -= c*A[i,:]
49 |
50 | def lu_decomposition(A):
51 | """ compute LU decomposition
52 |
53 | Args:
54 |
55 | A (ndarray): input matrix
56 |
57 | Returns:
58 |
59 | L (ndarray): lower triangular matrix
60 | U (ndarray): upper triangular matrix
61 |
62 | """
63 |
64 | n = len(A)
65 |
66 | # a. create zero matrices for L and U
67 | L = np.zeros((n,n))
68 | U = np.zeros((n,n))
69 |
70 | # b. set diagonal of L to one
71 | np.fill_diagonal(L,1)
72 |
73 | # c. perform the LU Decomposition
74 | for j in range(n):
75 |
76 | for i in range(j+1):
77 | c = U[:,j]@L[i,:]
78 | U[i][j] = A[i][j] - c
79 |
80 | for i in range(j, n):
81 | c = U[:j,j]@L[i,:j]
82 | L[i][j] = (A[i][j] - c) / U[j][j]
83 |
84 | return L,U
85 |
86 | def solve_with_forward_substitution(L,RHS):
87 | """ solve matrix equation with forward substitution
88 |
89 | Args:
90 |
91 | L (ndarray): lower triangular matrix
92 | RHS (ndarray): vector of right-hand-side variables
93 |
94 | Returns:
95 |
96 | x (ndarray): Solution vector
97 |
98 | """
99 |
100 | n = RHS.size
101 | x = np.zeros(n)
102 | for i in range(n):
103 | x[i] = RHS[i]
104 | for j in range(i):
105 | x[i] -= L[i,j]*x[j]
106 | x[i] /= L[i,i]
107 |
108 | return x
109 |
110 | def solve_with_backward_substitution(U,RHS):
111 | """ solve matrix equation with backward substitution
112 |
113 | Args:
114 |
115 | L (ndarray): uppper triangular matrix
116 | RHS (ndarray): vector of right-hand-side variables
117 |
118 | Returns:
119 |
120 | x (ndarray): Solution vector
121 |
122 | """
123 |
124 | n = RHS.size
125 | x = np.zeros(n)
126 | for i in reversed(range(n)):
127 | x[i] = RHS[i]
128 | for j in range(i+1,n):
129 | x[i] -= U[i,j]*x[j]
130 | x[i] /= U[i,i]
131 |
132 | return x
133 |
134 | def gauss_seidel_split(A):
135 | """ split A matrix in additive lower and upper triangular matrices
136 |
137 | Args:
138 |
139 | A (ndarray): input matrix
140 |
141 | Returns:
142 |
143 | L (ndarray): lower triangular matrix
144 | U (ndarray): upper triangular matrix (zero diagonal)
145 |
146 | """
147 |
148 | L = np.tril(A)
149 | U = np.triu(A)
150 | np.fill_diagonal(U,0)
151 | return L,U
152 |
153 | def gauss_seidel(A,b,x0,max_iter=500,tau=10**(-8),do_print=False):
154 | """ solve matrix equation with Gauss-Seidel
155 |
156 | Args:
157 |
158 | A (ndarray): LHS matrix
159 | b (ndarray): RHS vector
160 | x0 (ndarray): guess on solution
161 | max_iter (int): maximum number of iterations (optional)
162 | tau (float): tolerance level
163 | do_print (bool): indicator for whether to print or not
164 |
165 | Returns:
166 |
167 | x (ndarray): Solution vector
168 |
169 | """
170 |
171 | converged = False
172 |
173 | # a. split
174 | L,U = gauss_seidel_split(A)
175 |
176 | # b. iterate
177 | x = x0
178 | i = 0
179 |
180 | if do_print:
181 | print(' ',x)
182 |
183 | while i < max_iter and not converged:
184 |
185 | # i. save previous
186 | x_prev = x
187 |
188 | # ii. compute RHS
189 | y = b-U@x
190 |
191 | # iii. solve with forward substituion
192 | x = solve_with_forward_substitution(L,y)
193 | #x = sp.linalg.solve_triangular(L,y,lower=True) # equivalent, but faster
194 |
195 | # iv. check convergence
196 | max_abs_diff = np.max(np.abs(x-x_prev))
197 | if max_abs_diff < tau:
198 | converged = True
199 |
200 | # v. misc
201 | if do_print:
202 | print(f'{i:3d}: [{x[0]:12.8f} {x[1]:12.8f} {x[2]:12.8f}]')
203 |
204 | i += 1
205 |
206 | return x
--------------------------------------------------------------------------------
/3 - Sorting/bubblesort.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NumEconCopenhagen/IntroProg-lectures/bfd33808dbfc7bf3e60851b608c50d1bb0fbb5c2/3 - Sorting/bubblesort.png
--------------------------------------------------------------------------------
/3 - Sorting/insertionsort.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NumEconCopenhagen/IntroProg-lectures/bfd33808dbfc7bf3e60851b608c50d1bb0fbb5c2/3 - Sorting/insertionsort.png
--------------------------------------------------------------------------------
/3 - Sorting/quicksort.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NumEconCopenhagen/IntroProg-lectures/bfd33808dbfc7bf3e60851b608c50d1bb0fbb5c2/3 - Sorting/quicksort.png
--------------------------------------------------------------------------------
/4 - Model project lecture/Conflictmodel.py:
--------------------------------------------------------------------------------
1 |
2 | # Import packages (just the ones you need)
3 | import numpy as np
4 | import matplotlib.pyplot as plt
5 | from types import SimpleNamespace
6 | from scipy import optimize, interpolate
7 | plt.rcParams.update({"axes.grid":True,"grid.color":"black","grid.alpha":"0.25","grid.linestyle":"--"})
8 | plt.rcParams.update({'font.size': 14})
9 |
10 |
11 | # Define the model
12 | class ConflictModel():
13 |
14 | def __init__(self, **kwargs):
15 | '''
16 | Initialize the model with default parameters
17 | kwargs allow any parameter in the par namespace to be overridden by the user
18 | '''
19 |
20 | self.par = par = SimpleNamespace() # Create a namespace object for parameters
21 | self.sol = sol = SimpleNamespace() # Create a namespace object for solution results
22 |
23 |
24 | # Set default parameters
25 | self.setup()
26 |
27 |
28 |
29 | # Update parameters with user input
30 | for key, value in kwargs.items():
31 | setattr(par, key, value)
32 |
33 |
34 | def setup(self):
35 | '''
36 | Set default parameters
37 | '''
38 | par = self.par
39 |
40 | # Model parameters
41 | par.ea = 10
42 | par.eb = 10
43 | par.epsilon = 10.
44 | par.eta = 1000.
45 |
46 | par.small = 1e-4
47 |
--------------------------------------------------------------------------------
/4 - Model project lecture/Conflictmodel_sol.py:
--------------------------------------------------------------------------------
1 |
2 | # Import packages (just the ones you need)
3 | import numpy as np
4 | import matplotlib.pyplot as plt
5 | from types import SimpleNamespace
6 | from scipy import optimize, interpolate
7 | plt.rcParams.update({"axes.grid":True,"grid.color":"black","grid.alpha":"0.25","grid.linestyle":"--"})
8 | plt.rcParams.update({'font.size': 14})
9 |
10 |
11 |
12 |
13 | # Define the model
14 | class ConflictModel():
15 |
16 | def __init__(self, **kwargs):
17 | '''
18 | Initialize the model with default parameters
19 | kwargs allow any parameter in the par namespace to be overridden by the user
20 | '''
21 |
22 | self.par = par = SimpleNamespace() # Create a namespace object for parameters
23 | self.sol = sol = SimpleNamespace() # Create a namespace object for solution results
24 |
25 |
26 | # Set default parameters
27 | self.setup()
28 |
29 | # Set default utility functions
30 | self.u = self.u_ql
31 |
32 | # Update parameters with user input
33 | for key, value in kwargs.items():
34 | setattr(par, key, value)
35 |
36 |
37 |
38 | def setup(self):
39 | '''
40 | Set default parameters
41 | '''
42 | par = self.par
43 | sol = self.sol
44 |
45 | # Model parameters
46 | par.ea = 10
47 | par.eb = 10
48 | par.epsilon = 10.
49 | par.eta = 1000.
50 |
51 | par.small = 1e-4
52 |
53 |
54 |
55 | def u_ql(self,c,cm):
56 | '''
57 | Utility function
58 | '''
59 | par = self.par
60 | return c + cm**(1-1/par.epsilon)/(1-1/par.epsilon)
61 |
62 | def u_nonlinear(self,c,cm):
63 | par = self.par
64 | return c**(1-1/par.eta)/(1-1/par.eta) + cm**(1-1/par.epsilon)/(1-1/par.epsilon)
65 |
66 |
67 |
68 | def solve_A(self,p):
69 | par = self.par
70 |
71 | def obj(cm):
72 | return -self.u(par.ea-p*cm,cm)
73 |
74 | res = optimize.minimize_scalar(obj,bounds=(par.small ,par.ea/p-par.small),method='bounded')
75 | return res.x
76 |
77 |
78 | def solve_A_analytical(self,p):
79 | par = self.par
80 | return p**(-par.epsilon)
81 |
82 |
83 |
84 | def solve_A_grid(self):
85 | par = self.par
86 | sol = self.sol
87 |
88 | sol.p_vec = np.linspace(1,2,500)
89 | sol.D_vec = np.empty_like(sol.p_vec)
90 |
91 | for i, p in enumerate(sol.p_vec):
92 | sol.D_vec[i] = self.solve_A(p)
93 |
94 | sol.D_func = interpolate.RegularGridInterpolator([sol.p_vec],sol.D_vec,method='linear')
95 |
96 |
97 |
98 |
99 | def solve_B(self,method='optimize'):
100 | par = self.par
101 | sol = self.sol
102 |
103 | if method in ['analytical']:
104 | D_func = lambda p : self.solve_A_analytical(p)
105 |
106 |
107 | elif method in ['optimize']:
108 | D_func = lambda p : self.solve_A(p)
109 |
110 |
111 |
112 | elif method in ['interpolate']:
113 |
114 | self.solve_A_grid()
115 | D_func = lambda p : sol.D_func([p]).item()
116 |
117 | def obj(p):
118 | Dp = D_func(p)
119 | c = np.fmax(par.eb-Dp,par.small)
120 | cm = np.fmax(p*Dp,par.small)
121 |
122 | return -self.u(c,cm)
123 |
124 | res = optimize.minimize_scalar(obj,bounds=(1,2),method='bounded')
125 | return res.x
126 |
127 | def solve_B_analytical(self):
128 | par = self.par
129 | return (par.epsilon/(par.epsilon-1))**(par.epsilon/(2*par.epsilon-1))
130 |
131 |
132 |
133 | def plot_solve_A(self):
134 | par = self.par
135 |
136 | p = np.linspace(1,2,100)
137 |
138 | cm = np.empty_like(p)
139 | cm_a = np.empty_like(p)
140 |
141 | for i,pi in enumerate(p):
142 | cm[i] = self.solve_A(pi)
143 | cm_a[i] = self.solve_A_analytical(pi)
144 |
145 |
146 | fig, ax = plt.subplots()
147 | ax.plot(p,cm,label='Numerical')
148 | ax.plot(p,cm_a,label='Analytical',linestyle='--')
149 | ax.legend()
150 | ax.set_xlabel('Price')
151 | ax.set_ylabel("Consumption of $c'$")
152 | ax.set_title("Optimal consumption of $c'$ for A as a function of price")
153 | plt.show()
154 |
155 |
156 |
157 | def plot_solve_B(self,xrange= [5,15]):
158 | par = self.par
159 |
160 | epsilon_org = par.epsilon
161 |
162 |
163 | epsilon_vec = np.linspace(*xrange,100)
164 |
165 | p_vec = np.empty_like(epsilon_vec)
166 | p_a_vec = np.empty_like(epsilon_vec)
167 |
168 |
169 | for i,epsilon in enumerate(epsilon_vec):
170 | par.epsilon = epsilon
171 | p_vec[i] = self.solve_B()
172 | p_a_vec[i] = self.solve_B_analytical()
173 |
174 | par.epsilon = epsilon_org
175 |
176 | fig, ax = plt.subplots()
177 | ax.plot(epsilon_vec,p_vec,label='Numerical')
178 | ax.plot(epsilon_vec,p_a_vec,label='Analytical',linestyle='--')
179 | ax.legend()
180 | ax.set_xlabel('Epsilon')
181 | ax.set_ylabel("Price")
182 | ax.set_title("Price as a function of epsilon for B")
183 |
184 |
185 | def plot_solve_B_range_ea(self):
186 | par = self.par
187 |
188 | ea_org = par.ea
189 |
190 | ea_vec = np.linspace(2.5,30,100)
191 |
192 | p_vec = np.empty_like(ea_vec)
193 |
194 | for i,ea in enumerate(ea_vec):
195 | par.ea = ea
196 | p_vec[i] = self.solve_B()
197 |
198 |
199 | par.ea = ea_org
200 |
201 | fig, ax = plt.subplots()
202 | ax.plot(ea_vec,p_vec,label='Numerical')
203 | ax.legend()
204 | ax.set_xlabel('Ea')
205 | ax.set_ylabel("Price")
206 | ax.set_title("Price as a function of ea for B in the range 2.5 to 30")
207 | plt.show()
--------------------------------------------------------------------------------
/4 - Model project lecture/ExchangeEconomy.py:
--------------------------------------------------------------------------------
1 |
2 | # Import packages (just the ones you need)
3 | import numpy as np
4 | import matplotlib.pyplot as plt
5 | from types import SimpleNamespace
6 | from scipy import optimize
7 | plt.rcParams.update({"axes.grid":True,"grid.color":"black","grid.alpha":"0.25","grid.linestyle":"--"})
8 | plt.rcParams.update({'font.size': 14})
9 |
10 |
11 | class ExchangeEconomyModel:
12 | def __init__(self, **kwargs):
13 | '''
14 | Initialize the model with default parameters
15 | kwargs allow any parameter in the par namespace to be overridden by the user
16 | '''
17 |
18 | self.par = par = SimpleNamespace() # Create a namespace object for parameters
19 | self.sol = sol = SimpleNamespace() # Create a namespace object for solution results
20 | self.sim = sim = SimpleNamespace() # Create a namespace object for simulation results (not always neccesary)
21 |
22 | # Set default parameters
23 | self.setup()
24 |
25 | # Update parameters with user input
26 | for key, value in kwargs.items():
27 | setattr(par, key, value)
28 |
29 | # Allocate arrays simulation (if needed)
30 | self.allocate()
31 |
32 | # Simulate draws
33 | self.simulate()
34 |
35 |
36 | def setup(self):
37 | '''
38 | Set default parameters
39 | '''
40 | par = self.par
41 |
42 | # a. parameters
43 | par.N = 50
44 | par.mu = np.array([3,2,1])
45 | par.Sigma = np.array([[0.25, 0, 0], [0, 0.25, 0], [0, 0, 0.25]])
46 | par.gamma = 0.8
47 | par.zeta = 1
48 |
49 | par.epsilon = 1e-4
50 | par.kappa = 4
51 |
52 |
53 |
54 |
55 | def allocate(self):
56 | '''
57 | Allocate arrays for simulation
58 | '''
59 |
60 |
61 | par = self.par
62 | sim = self.sim
63 |
64 | simvarnames = ['e1','e2','e3'] # Which arrays should be available in the simulation namespace
65 |
66 | for varname in simvarnames:
67 | sim.__dict__[varname] = np.nan*np.ones(par.N) # Allocate the size of the arrays
68 |
69 |
70 | N3_vars = ['alphas','betas']
71 | for varname in N3_vars:
72 | sim.__dict__[varname] = np.nan*np.ones((par.N,3))
73 |
74 |
75 | def simulate(self,seed=1234):
76 | '''
77 | Draw random shocks for your model
78 |
79 | It's a good idea to draw all the random shocks you want in one function and store them
80 | You can then be explicit about when you change the seed and take a new draw.
81 | '''
82 | par = self.par
83 | sim = self.sim
84 |
85 | np.random.seed(seed)
86 |
87 | # preferences
88 | sim.alphas[:] = np.exp(np.random.multivariate_normal(par.mu, par.Sigma, size=par.N))
89 | sim.betas[:] = sim.alphas/np.reshape(np.sum(sim.alphas,axis=1),(par.N,1))
90 |
91 | # endowments
92 | sim.e1[:] = np.random.exponential(par.zeta,size=par.N)
93 | sim.e2[:] = np.random.exponential(par.zeta,size=par.N)
94 | sim.e3[:] = np.random.exponential(par.zeta,size=par.N)
95 |
96 |
--------------------------------------------------------------------------------
/4 - Model project lecture/ModelTemplate.py:
--------------------------------------------------------------------------------
1 | ### This is a template/example of how to structure the .py-file for a model
2 |
3 |
4 | # Import packages (just the ones you need)
5 | import numpy as np
6 | import matplotlib.pyplot as plt
7 | from types import SimpleNamespace
8 | from scipy import optimize
9 | plt.rcParams.update({"axes.grid":True,"grid.color":"black","grid.alpha":"0.25","grid.linestyle":"--"})
10 | plt.rcParams.update({'font.size': 14})
11 |
12 |
13 |
14 |
15 | # Define the model
16 | class ModelName():
17 |
18 | def __init__(self, **kwargs):
19 | '''
20 | Initialize the model with default parameters
21 | kwargs allow any parameter in the par namespace to be overridden by the user
22 | '''
23 |
24 | self.par = par = SimpleNamespace() # Create a namespace object for parameters
25 | self.sol = sol = SimpleNamespace() # Create a namespace object for solution results
26 | self.sim = sim = SimpleNamespace() # Create a namespace object for simulation results (not always neccesary)
27 |
28 | # Set default parameters
29 | self.setup()
30 |
31 | # Update parameters with user input
32 | for key, value in kwargs.items():
33 | setattr(par, key, value)
34 |
35 | # Allocate arrays simulation (if needed)
36 | self.allocate()
37 |
38 | # Draw shocks
39 | self.simulate()
40 |
41 |
42 | def setup(self):
43 | '''
44 | Set default parameters
45 | '''
46 | par = self.par
47 |
48 | # Model parameters
49 | par.alpha = 1.
50 |
51 | # Simulation options
52 | par.T = 100
53 |
54 |
55 |
56 | def allocate(self):
57 | '''
58 | Allocate arrays for simulation
59 | '''
60 |
61 |
62 | par = self.par
63 | sim = self.sim
64 |
65 | simvarnames = ['epsilon'] # Which arrays should be available in the simulation namespace
66 |
67 | for varname in simvarnames:
68 | sim.__dict__[varname] = np.nan*np.ones(par.T) # Allocate the size of the arrays
69 | # In this function all arrays have the same size, this is not always the case
70 |
71 |
72 |
73 | def simulate(self,seed=1234):
74 | '''
75 | Draw random shocks for your model
76 |
77 | It's a good idea to draw all the random shocks you want in one function and store them
78 | You can then be explicit about when you change the seed and take a new draw.
79 | '''
80 | par = self.par
81 | sim = self.sim
82 |
83 | np.random.seed(seed)
84 |
85 | sim.epsilon[:] = np.random.normal(size=par.T)
--------------------------------------------------------------------------------
/4 - OLG/OLGModel.py:
--------------------------------------------------------------------------------
1 | from types import SimpleNamespace
2 | import time
3 | import numpy as np
4 | from scipy import optimize
5 |
6 | class OLGModelClass():
7 |
8 | def __init__(self,do_print=True):
9 | """ create the model """
10 |
11 | if do_print: print('initializing the model:')
12 |
13 | self.par = SimpleNamespace()
14 | self.sim = SimpleNamespace()
15 |
16 | if do_print: print('calling .setup()')
17 | self.setup()
18 |
19 | if do_print: print('calling .allocate()')
20 | self.allocate()
21 |
22 | def setup(self):
23 | """ baseline parameters """
24 |
25 | par = self.par
26 |
27 | # a. household
28 | par.sigma = 2.0 # CRRA coefficient
29 | par.beta = 1/1.40 # discount factor
30 |
31 | # b. firms
32 | par.production_function = 'ces'
33 | par.alpha = 0.30 # capital weight
34 | par.theta = 0.05 # substitution parameter
35 | par.delta = 0.50 # depreciation rate
36 |
37 | # c. government
38 | par.tau_w = 0.10 # labor income tax
39 | par.tau_r = 0.20 # capital income tax
40 |
41 | # d. misc
42 | par.K_lag_ini = 1.0 # initial capital stock
43 | par.B_lag_ini = 0.0 # initial government debt
44 | par.simT = 50 # length of simulation
45 |
46 | def allocate(self):
47 | """ allocate arrays for simulation """
48 |
49 | par = self.par
50 | sim = self.sim
51 |
52 | # a. list of variables
53 | household = ['C1','C2']
54 | firm = ['K','Y','K_lag']
55 | prices = ['w','rk','rb','r','rt']
56 | government = ['G','T','B','balanced_budget','B_lag']
57 |
58 | # b. allocate
59 | allvarnames = household + firm + prices + government
60 | for varname in allvarnames:
61 | sim.__dict__[varname] = np.nan*np.ones(par.simT)
62 |
63 | def simulate(self,do_print=True):
64 | """ simulate model """
65 |
66 | t0 = time.time()
67 |
68 | par = self.par
69 | sim = self.sim
70 |
71 | # a. initial values
72 | sim.K_lag[0] = par.K_lag_ini
73 | sim.B_lag[0] = par.B_lag_ini
74 |
75 | # b. iterate
76 | for t in range(par.simT):
77 |
78 | # i. simulate before s
79 | simulate_before_s(par,sim,t)
80 |
81 | if t == par.simT-1: continue
82 |
83 | # i. find bracket to search
84 | s_min,s_max = find_s_bracket(par,sim,t)
85 |
86 | # ii. find optimal s
87 | obj = lambda s: calc_euler_error(s,par,sim,t=t)
88 | result = optimize.root_scalar(obj,bracket=(s_min,s_max),method='bisect')
89 | s = result.root
90 |
91 | # iii. simulate after s
92 | simulate_after_s(par,sim,t,s)
93 |
94 | if do_print: print(f'simulation done in {time.time()-t0:.2f} secs')
95 |
96 | def find_s_bracket(par,sim,t,maxiter=500,do_print=False):
97 | """ find bracket for s to search in """
98 |
99 | # a. maximum bracket
100 | s_min = 0.0 + 1e-8 # save almost nothing
101 | s_max = 1.0 - 1e-8 # save almost everything
102 |
103 | # b. saving a lot is always possible
104 | value = calc_euler_error(s_max,par,sim,t)
105 | sign_max = np.sign(value)
106 | if do_print: print(f'euler-error for s = {s_max:12.8f} = {value:12.8f}')
107 |
108 | # c. find bracket
109 | lower = s_min
110 | upper = s_max
111 |
112 | it = 0
113 | while it < maxiter:
114 |
115 | # i. midpoint and value
116 | s = (lower+upper)/2 # midpoint
117 | value = calc_euler_error(s,par,sim,t)
118 |
119 | if do_print: print(f'euler-error for s = {s:12.8f} = {value:12.8f}')
120 |
121 | # ii. check conditions
122 | valid = not np.isnan(value)
123 | correct_sign = np.sign(value)*sign_max < 0
124 |
125 | # iii. next step
126 | if valid and correct_sign: # found!
127 | s_min = s
128 | s_max = upper
129 | if do_print:
130 | print(f'bracket to search in with opposite signed errors:')
131 | print(f'[{s_min:12.8f}-{s_max:12.8f}]')
132 | return s_min,s_max
133 | elif not valid: # too low s -> increase lower bound
134 | lower = s
135 | else: # too high s -> increase upper bound
136 | upper = s
137 |
138 | # iv. increment
139 | it += 1
140 |
141 | raise Exception('cannot find bracket for s')
142 |
143 | def calc_euler_error(s,par,sim,t):
144 | """ target function for finding s with bisection """
145 |
146 | # a. simulate forward
147 | simulate_after_s(par,sim,t,s)
148 | simulate_before_s(par,sim,t+1) # next period
149 |
150 | # c. Euler equation
151 | LHS = sim.C1[t]**(-par.sigma)
152 | RHS = (1+sim.rt[t+1])*par.beta * sim.C2[t+1]**(-par.sigma)
153 |
154 | return LHS-RHS
155 |
156 | def simulate_before_s(par,sim,t):
157 | """ simulate forward """
158 |
159 | if t > 0:
160 | sim.K_lag[t] = sim.K[t-1]
161 | sim.B_lag[t] = sim.B[t-1]
162 |
163 | # a. production and factor prices
164 | if par.production_function == 'ces':
165 |
166 | # i. production
167 | sim.Y[t] = ( par.alpha*sim.K_lag[t]**(-par.theta) + (1-par.alpha)*(1.0)**(-par.theta) )**(-1.0/par.theta)
168 |
169 | # ii. factor prices
170 | sim.rk[t] = par.alpha*sim.K_lag[t]**(-par.theta-1) * sim.Y[t]**(1.0+par.theta)
171 | sim.w[t] = (1-par.alpha)*(1.0)**(-par.theta-1) * sim.Y[t]**(1.0+par.theta)
172 |
173 | elif par.production_function == 'cobb-douglas':
174 |
175 | # i. production
176 | sim.Y[t] = sim.K_lag[t]**par.alpha * (1.0)**(1-par.alpha)
177 |
178 | # ii. factor prices
179 | sim.rk[t] = par.alpha * sim.K_lag[t]**(par.alpha-1) * (1.0)**(1-par.alpha)
180 | sim.w[t] = (1-par.alpha) * sim.K_lag[t]**(par.alpha) * (1.0)**(-par.alpha)
181 |
182 | else:
183 |
184 | raise NotImplementedError('unknown type of production function')
185 |
186 | # b. no-arbitrage and after-tax return
187 | sim.r[t] = sim.rk[t]-par.delta # after-depreciation return
188 | sim.rb[t] = sim.r[t] # same return on bonds
189 | sim.rt[t] = (1-par.tau_r)*sim.r[t] # after-tax return
190 |
191 | # c. consumption
192 | sim.C2[t] = (1+sim.rt[t])*(sim.K_lag[t]+sim.B_lag[t])
193 |
194 | # d. government
195 | sim.T[t] = par.tau_r*sim.r[t]*(sim.K_lag[t]+sim.B_lag[t]) + par.tau_w*sim.w[t]
196 |
197 | if sim.balanced_budget[t]:
198 | sim.G[t] = sim.T[t] - sim.r[t]*sim.B_lag[t]
199 |
200 | sim.B[t] = (1+sim.r[t])*sim.B_lag[t] - sim.T[t] + sim.G[t]
201 |
202 | def simulate_after_s(par,sim,t,s):
203 | """ simulate forward """
204 |
205 | # a. consumption of young
206 | sim.C1[t] = (1-par.tau_w)*sim.w[t]*(1.0-s)
207 |
208 | # b. end-of-period stocks
209 | I = sim.Y[t] - sim.C1[t] - sim.C2[t] - sim.G[t]
210 | sim.K[t] = (1-par.delta)*sim.K_lag[t] + I
--------------------------------------------------------------------------------
/4 - Ramsey/RamseyModel.py:
--------------------------------------------------------------------------------
1 | from types import SimpleNamespace
2 | import numpy as np
3 | from scipy import optimize
4 |
5 | class RamseyModelClass():
6 |
7 | def __init__(self,do_print=True):
8 | """ create the model """
9 |
10 | if do_print: print('initializing the model:')
11 |
12 | self.par = SimpleNamespace()
13 | self.ss = SimpleNamespace()
14 | self.path = SimpleNamespace()
15 |
16 | if do_print: print('calling .setup()')
17 | self.setup()
18 |
19 | if do_print: print('calling .allocate()')
20 | self.allocate()
21 |
22 | def setup(self):
23 | """ baseline parameters """
24 |
25 | par = self.par
26 |
27 | # a. household
28 | par.sigma = 2.0 # CRRA coefficient
29 | par.beta = np.nan # discount factor
30 |
31 | # b. firms
32 | par.Gamma = np.nan
33 | par.production_function = 'cobb-douglas'
34 | par.alpha = 0.30 # capital weight
35 | par.theta = 0.05 # substitution parameter
36 | par.delta = 0.05 # depreciation rate
37 |
38 | # c. initial
39 | par.K_lag_ini = 1.0
40 |
41 | # d. misc
42 | par.solver = 'broyden' # solver for the equation system, 'broyden' or 'scipy'
43 | par.Tpath = 500 # length of transition path, "truncation horizon"
44 |
45 | def allocate(self):
46 | """ allocate arrays for transition path """
47 |
48 | par = self.par
49 | path = self.path
50 |
51 | allvarnames = ['Gamma','K','C','rk','w','r','Y','K_lag']
52 | for varname in allvarnames:
53 | path.__dict__[varname] = np.nan*np.ones(par.Tpath)
54 |
55 | def find_steady_state(self,KY_ss,do_print=True):
56 | """ find steady state """
57 |
58 | par = self.par
59 | ss = self.ss
60 |
61 | # a. find A
62 | ss.K = KY_ss
63 | Y,_,_ = production(par,1.0,ss.K)
64 | ss.Gamma = 1/Y
65 |
66 | # b. factor prices
67 | ss.Y,ss.rk,ss.w = production(par,ss.Gamma,ss.K)
68 | assert np.isclose(ss.Y,1.0)
69 |
70 | ss.r = ss.rk-par.delta
71 |
72 | # c. implied discount factor
73 | par.beta = 1/(1+ss.r)
74 |
75 | # d. consumption
76 | ss.C = ss.Y - par.delta*ss.K
77 |
78 | if do_print:
79 |
80 | print(f'Y_ss = {ss.Y:.4f}')
81 | print(f'K_ss/Y_ss = {ss.K/ss.Y:.4f}')
82 | print(f'rk_ss = {ss.rk:.4f}')
83 | print(f'r_ss = {ss.r:.4f}')
84 | print(f'w_ss = {ss.w:.4f}')
85 | print(f'Gamma = {ss.Gamma:.4f}')
86 | print(f'beta = {par.beta:.4f}')
87 |
88 | def evaluate_path_errors(self):
89 | """ evaluate errors along transition path """
90 |
91 | par = self.par
92 | ss = self.ss
93 | path = self.path
94 |
95 | # a. consumption
96 | C = path.C
97 | C_plus = np.append(path.C[1:],ss.C)
98 |
99 | # b. capital
100 | K = path.K
101 | K_lag = path.K_lag = np.insert(K[:-1],0,par.K_lag_ini)
102 |
103 | # c. production and factor prices
104 | path.Y,path.rk,path.w = production(par,path.Gamma,K_lag)
105 | path.r = path.rk-par.delta
106 | r_plus = np.append(path.r[1:],ss.r)
107 |
108 | # d. errors (also called H)
109 | errors = np.nan*np.ones((2,par.Tpath))
110 | errors[0,:] = C**(-par.sigma) - par.beta*(1+r_plus)*C_plus**(-par.sigma)
111 | errors[1,:] = K - ((1-par.delta)*K_lag + (path.Y - C))
112 |
113 | return errors.ravel()
114 |
115 | def calculate_jacobian(self,h=1e-6):
116 | """ calculate jacobian """
117 |
118 | par = self.par
119 | ss = self.ss
120 | path = self.path
121 |
122 | # a. allocate
123 | Njac = 2*par.Tpath
124 | jac = self.jac = np.nan*np.ones((Njac,Njac))
125 |
126 | x_ss = np.nan*np.ones((2,par.Tpath))
127 | x_ss[0,:] = ss.C
128 | x_ss[1,:] = ss.K
129 | x_ss = x_ss.ravel()
130 |
131 | # b. baseline errors
132 | path.C[:] = ss.C
133 | path.K[:] = ss.K
134 | base = self.evaluate_path_errors()
135 |
136 | # c. jacobian
137 | for i in range(Njac):
138 |
139 | # i. add small number to a single x (single K or C)
140 | x_jac = x_ss.copy()
141 | x_jac[i] += h
142 | x_jac = x_jac.reshape((2,par.Tpath))
143 |
144 | # ii. alternative errors
145 | path.C[:] = x_jac[0,:]
146 | path.K[:] = x_jac[1,:]
147 | alt = self.evaluate_path_errors()
148 |
149 | # iii. numerical derivative
150 | jac[:,i] = (alt-base)/h
151 |
152 | def solve(self,do_print=True):
153 | """ solve for the transition path """
154 |
155 | par = self.par
156 | ss = self.ss
157 | path = self.path
158 |
159 | # a. equation system
160 | def eq_sys(x):
161 |
162 | # i. update
163 | x = x.reshape((2,par.Tpath))
164 | path.C[:] = x[0,:]
165 | path.K[:] = x[1,:]
166 |
167 | # ii. return errors
168 | return self.evaluate_path_errors()
169 |
170 | # b. initial guess
171 | x0 = np.nan*np.ones((2,par.Tpath))
172 | x0[0,:] = ss.C
173 | x0[1,:] = ss.K
174 | x0 = x0.ravel()
175 |
176 | # c. call solver
177 | if par.solver == 'broyden':
178 |
179 | x = broyden_solver(eq_sys,x0,self.jac,do_print=do_print)
180 |
181 | elif par.solver == 'scipy':
182 |
183 | root = optimize.root(eq_sys,x0,method='hybr',options={'factor':1.0})
184 | # the factor determines the size of the initial step
185 | # too low: slow
186 | # too high: prone to errors
187 |
188 | x = root.x
189 |
190 | else:
191 |
192 | raise NotImplementedError('unknown solver')
193 |
194 |
195 | # d. final evaluation
196 | eq_sys(x)
197 |
198 | def production(par,Gamma,K_lag):
199 | """ production and factor prices """
200 |
201 | # a. production and factor prices
202 | if par.production_function == 'ces':
203 |
204 | # a. production
205 | Y = Gamma*( par.alpha*K_lag**(-par.theta) + (1-par.alpha)*(1.0)**(-par.theta) )**(-1.0/par.theta)
206 |
207 | # b. factor prices
208 | rk = Gamma*par.alpha*K_lag**(-par.theta-1) * (Y/Gamma)**(1.0+par.theta)
209 | w = Gamma*(1-par.alpha)*(1.0)**(-par.theta-1) * (Y/Gamma)**(1.0+par.theta)
210 |
211 | elif par.production_function == 'cobb-douglas':
212 |
213 | # a. production
214 | Y = Gamma*K_lag**par.alpha * (1.0)**(1-par.alpha)
215 |
216 | # b. factor prices
217 | rk = Gamma*par.alpha * K_lag**(par.alpha-1) * (1.0)**(1-par.alpha)
218 | w = Gamma*(1-par.alpha) * K_lag**(par.alpha) * (1.0)**(-par.alpha)
219 |
220 | else:
221 |
222 | raise Exception('unknown type of production function')
223 |
224 | return Y,rk,w
225 |
226 | def broyden_solver(f,x0,jac,tol=1e-8,maxiter=100,do_print=False):
227 | """ numerical equation system solver using the broyden method
228 |
229 | f (callable): function return errors in equation system
230 | jac (ndarray): initial jacobian
231 | tol (float,optional): tolerance
232 | maxiter (int,optional): maximum number of iterations
233 | do_print (bool,optional): print progress
234 |
235 | """
236 |
237 | # a. initial
238 | x = x0.ravel()
239 | y = f(x)
240 |
241 | # b. iterate
242 | for it in range(maxiter):
243 |
244 | # i. current difference
245 | abs_diff = np.max(np.abs(y))
246 | if do_print: print(f' it = {it:3d} -> max. abs. error = {abs_diff:12.8f}')
247 |
248 | if abs_diff < tol: return x
249 |
250 | # ii. new x
251 | dx = np.linalg.solve(jac,-y)
252 | assert not np.any(np.isnan(dx))
253 |
254 | # iii. evaluate
255 | ynew = f(x+dx)
256 | dy = ynew-y
257 | jac = jac + np.outer(((dy - jac @ dx) / np.linalg.norm(dx)**2), dx)
258 | y = ynew
259 | x += dx
260 |
261 | else:
262 |
263 | raise ValueError(f'no convergence after {maxiter} iterations')
--------------------------------------------------------------------------------
/4 - Speed/computer.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NumEconCopenhagen/IntroProg-lectures/bfd33808dbfc7bf3e60851b608c50d1bb0fbb5c2/4 - Speed/computer.gif
--------------------------------------------------------------------------------
/4 - Speed/memory.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NumEconCopenhagen/IntroProg-lectures/bfd33808dbfc7bf3e60851b608c50d1bb0fbb5c2/4 - Speed/memory.gif
--------------------------------------------------------------------------------
/4 - Speed/moores_law.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NumEconCopenhagen/IntroProg-lectures/bfd33808dbfc7bf3e60851b608c50d1bb0fbb5c2/4 - Speed/moores_law.png
--------------------------------------------------------------------------------
/4 - Speed/mydask.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NumEconCopenhagen/IntroProg-lectures/bfd33808dbfc7bf3e60851b608c50d1bb0fbb5c2/4 - Speed/mydask.png
--------------------------------------------------------------------------------
/4 - Speed/needforspeed.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 |
3 | def create_list(n):
4 | return [i*i for i in range(n)]
5 |
6 | def create_generator(n):
7 | return (i*i for i in range(n))
8 |
9 | def test_memory(n):
10 |
11 | # list vs. generators
12 | squares = create_list(n)
13 | squares_generator = create_generator(n)
14 |
15 | # numpy arrays of different types
16 | A = np.ones((1000,1000))
17 | B = np.ones((1000,1000),dtype=np.double)
18 | C = np.ones((1000,1000),dtype=np.single)
19 | D = np.ones((1000,1000),dtype=np.int64)
20 | E = np.ones((1000,1000),dtype=np.int32)
21 | F = np.ones((1000,1000),dtype=np.bool)
--------------------------------------------------------------------------------
/4 - Speed/numpy_memory_layout.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NumEconCopenhagen/IntroProg-lectures/bfd33808dbfc7bf3e60851b608c50d1bb0fbb5c2/4 - Speed/numpy_memory_layout.png
--------------------------------------------------------------------------------
/4 - Structural estimation/ConsumptionSavingModel.py:
--------------------------------------------------------------------------------
1 | from types import SimpleNamespace
2 |
3 | import numpy as np
4 | from scipy import interpolate
5 | from scipy import optimize
6 |
7 | class ConsumptionSavingModelClass:
8 |
9 | def __init__(self):
10 |
11 | # a. namespaces
12 | par = self.par = SimpleNamespace()
13 | sol = self.sol = SimpleNamespace()
14 | sim = self.sim = SimpleNamespace()
15 |
16 | # b. parameters
17 | par.rho = 2.0
18 | par.kappa = 0.5
19 | par.nu = 10.0
20 | par.r = 0.04
21 | par.beta = 0.94
22 | par.simN = 1_000
23 |
24 | def utility(self,c):
25 | return c**(1-self.par.rho)/(1-self.par.rho)
26 |
27 | def bequest(self,m,c):
28 | return self.par.nu*(m-c+self.par.kappa)**(1-self.par.rho)/(1-self.par.rho)
29 |
30 | def v2(self,c2,m2):
31 | return self.utility(c2) + self.bequest(m2,c2)
32 |
33 | def v1(self,c1,m1,v2_interp):
34 |
35 | # a. v2 value, if low income
36 | m2_low = (1+self.par.r)*(m1-c1) + 0.5
37 | v2_low = v2_interp([m2_low])[0]
38 |
39 | # b. v2 value, if high income
40 | m2_high = (1+self.par.r)*(m1-c1) + 1.5
41 | v2_high = v2_interp([m2_high])[0]
42 |
43 | # c. expected v2 value
44 | expected_v2 = 0.5*v2_low + 0.5*v2_high
45 |
46 | # d. total value
47 | return self.utility(c1) + self.par.beta*expected_v2
48 |
49 | def solve_period_2(self):
50 |
51 | # a. grids
52 | m2s = np.linspace(1e-4,5,500)
53 | v2s = np.empty(500)
54 | c2s = np.empty(500)
55 |
56 | # b. solve for each m2 in grid
57 | for i,m2 in enumerate(m2s):
58 |
59 | # i. objective
60 | obj = lambda x: -self.v2(x[0],m2)
61 |
62 | # ii. initial value (consume half)
63 | x0 = m2/2
64 |
65 | # iii. optimizer
66 | result = optimize.minimize(obj,[x0],method='L-BFGS-B',bounds=((1e-8,m2),))
67 |
68 | # iv. save
69 | v2s[i] = -result.fun
70 | c2s[i] = result.x
71 |
72 | return m2s,v2s,c2s
73 |
74 | def solve_period_1(self, v2_interp):
75 |
76 | # a. grids
77 | m1s = np.linspace(1e-8,4,100)
78 | v1s = np.empty(100)
79 | c1s = np.empty(100)
80 |
81 | # b. solve for each m1s in grid
82 | for i, m1 in enumerate(m1s):
83 |
84 | # i. objective
85 | def obj(x): return -self.v1(x[0], m1, v2_interp)
86 |
87 | # ii. initial guess (consume half)
88 | x0 = m1/2
89 |
90 | # iii. optimize
91 | result = optimize.minimize(
92 | obj, [x0], method='L-BFGS-B', bounds=((1e-12, m1),))
93 |
94 | # iv. save
95 | v1s[i] = -result.fun
96 | c1s[i] = result.x[0]
97 |
98 | return m1s, v1s, c1s
99 |
100 | def solve(self):
101 | """ solve the model """
102 |
103 | sol = self.sol
104 |
105 | # a. solve period 2
106 | sol.m2, sol.v2, sol.c2 = self.solve_period_2()
107 |
108 | # b. construct interpolator
109 | v2_interp = interpolate.RegularGridInterpolator([sol.m2], sol.v2,
110 | bounds_error=False, fill_value=None)
111 |
112 | # b. solve period 1
113 | sol.m1,sol.v1,sol.c1 = self.solve_period_1(v2_interp)
114 |
115 | def draw_random_values(self):
116 | """ draw random values for simulation """
117 |
118 | par = self.par
119 | sim = self.sim
120 |
121 | sim.m1 = np.fmax(np.random.normal(1,0.1,size=par.simN),0)
122 | sim.y2 = np.random.choice([0.5,1.5],p=[0.5,0.5],size=(par.simN))
123 |
124 | def simulate(self):
125 | """ simulate the model """
126 |
127 | par = self.par
128 | sol = self.sol
129 | sim = self.sim
130 |
131 | # a. construct interpolaters
132 | c1_interp = interpolate.RegularGridInterpolator([sol.m1], sol.c1,
133 | bounds_error=False, fill_value=None)
134 |
135 | c2_interp = interpolate.RegularGridInterpolator([sol.m2], sol.c2,
136 | bounds_error=False, fill_value=None)
137 |
138 | # b. sim period 1 based on draws of initial m and solution
139 | sim.c1 = c1_interp(sim.m1)
140 | sim.a1 = sim.m1-sim.c1
141 |
142 | # c. transition to period 2 m based on random draws
143 | sim.m2 = (1+par.r)*sim.a1 + sim.y2
144 |
145 | # d. sim period 2 consumption choice based on model solution and sim.m2
146 | sim.c2 = c2_interp(sim.m2)
--------------------------------------------------------------------------------
/5 - Outroduction/Outroduction.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NumEconCopenhagen/IntroProg-lectures/bfd33808dbfc7bf3e60851b608c50d1bb0fbb5c2/5 - Outroduction/Outroduction.pdf
--------------------------------------------------------------------------------
/5 - Outroduction/PS6/numecon_linalg.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import scipy as sp
3 | import sympy as sm
4 |
5 | def gauss_jordan(A,no_reduction=False):
6 | """ Gauss-Jordan elimination
7 |
8 | Convert a matrix to its row (reduced) echelon form.
9 |
10 | Args:
11 |
12 | A (ndarray): input matrix to work on in-place
13 | no_reduction (bool): stop when non-reduced echelon form is reached
14 |
15 | """
16 |
17 | n,_m = A.shape
18 |
19 | # row echelon form (Gauss elimination)
20 | for i in range(0, n):
21 |
22 | # a. search for maximum in this column
23 | maxrow = i + np.argmax(abs(A[i:,i]))
24 |
25 | # b. swap maximum row with current row (column by column)
26 | temp = A[maxrow,i:].copy()
27 | A[maxrow,i:] = A[i,i:]
28 | A[i,i:] = temp
29 |
30 | # b. make all rows below this one 0 in current column
31 | for k in range(i+1, n):
32 | c = -A[k,i]/A[i,i]
33 | A[k,i] = 0
34 | A[k,i+1:] += c*A[i,i+1:]
35 |
36 | if no_reduction:
37 | return
38 |
39 | # reduced row echelon form (Gauss-Jordan elimination)
40 | for i in range(n-1,-1,-1):
41 |
42 | # a. normalize this row
43 | c = A[i,i]
44 | A[i,:] /= c
45 |
46 | # b. make all rows above this one 0 in the current column
47 | for j in range(0,i):
48 | c = A[j,i]
49 | A[j,:] -= c*A[i,:]
50 |
51 | def gauss_seidel_split(A):
52 | """ split A matrix in additive lower and upper triangular matrices
53 |
54 | Args:
55 |
56 | A (ndarray): input matrix
57 |
58 | Returns:
59 |
60 | L (ndarray): lower triangular matrix
61 | U (ndarray): upper triangular matrix (zero diagonal)
62 |
63 | """
64 |
65 | L = np.tril(A)
66 | U = np.triu(A)
67 | np.fill_diagonal(U,0)
68 | return L,U
69 |
70 | def solve_with_forward_substitution(L,RHS):
71 | """ solve matrix equation with forward substitution
72 |
73 | Args:
74 |
75 | L (ndarray): lower triangular matrix
76 | RHS (ndarray): vector of right-hand-side variables
77 |
78 | Returns:
79 |
80 | x (ndarray): Solution vector
81 |
82 | """
83 |
84 | n = RHS.size
85 | x = np.zeros(n)
86 | for i in range(n):
87 | x[i] = RHS[i]
88 | for j in range(i):
89 | x[i] -= L[i,j]*x[j]
90 | x[i] /= L[i,i]
91 |
92 | return x
93 |
94 | def solve_with_backward_substitution(U,RHS):
95 | """ solve matrix equation with backward substitution
96 |
97 | Args:
98 |
99 | L (ndarray): uppper triangular matrix
100 | RHS (ndarray): vector of right-hand-side variables
101 |
102 | Returns:
103 |
104 | x (ndarray): Solution vector
105 |
106 | """
107 |
108 | n = RHS.size
109 | x = np.zeros(n)
110 | for i in reversed(range(n)):
111 | x[i] = RHS[i]
112 | for j in range(i+1,n):
113 | x[i] -= U[i,j]*x[j]
114 | x[i] /= U[i,i]
115 |
116 | return x
117 |
118 | def gauss_seidel(A,b,x0,max_iter=500,tau=10**(-8),do_print=False):
119 | """ solve matrix equation with Gauss-Seidel
120 |
121 | Args:
122 |
123 | A (ndarray): LHS matrix
124 | b (ndarray): RHS vector
125 | x0 (ndarray): guess on solution
126 | max_iter (int): maximum number of iterations (optional)
127 | tau (float): tolerance level
128 | do_print (bool): indicator for whether to print or not
129 |
130 | Returns:
131 |
132 | x (ndarray): Solution vector
133 |
134 | """
135 |
136 | converged = False
137 |
138 | # a. split
139 | L,U = gauss_seidel_split(A)
140 |
141 | # b. iterate
142 | x = x0
143 | i = 0
144 |
145 | if do_print:
146 | print(' ',x)
147 |
148 | while i < max_iter and not converged:
149 |
150 | # i. save previous
151 | x_prev = x
152 |
153 | # ii. compute RHS
154 | y = b-U@x
155 |
156 | # iii. solve with forward substituion
157 | x = solve_with_forward_substitution(L,y)
158 | #x = sp.linalg.solve_triangular(L,y,lower=True) # equivalent, but faster
159 |
160 | # iv. check convergence
161 | max_abs_diff = np.max(np.abs(x-x_prev))
162 | if max_abs_diff < tau:
163 | converged = True
164 |
165 | # v. misc
166 | if do_print:
167 | print(f'{i:2d}',x)
168 |
169 | i += 1
170 |
171 | return x
172 |
173 | def lu_decomposition(A):
174 | """ compute LU decomposition
175 |
176 | Args:
177 |
178 | A (ndarray): input matrix
179 |
180 | Returns:
181 |
182 | L (ndarray): lower triangular matrix
183 | U (ndarray): upper triangular matrix
184 |
185 | """
186 |
187 | n = len(A)
188 |
189 | # a. create zero matrices for L and U
190 | L = np.zeros((n,n))
191 | U = np.zeros((n,n))
192 |
193 | # b. set diagonal of L to one
194 | np.fill_diagonal(L,1)
195 |
196 | # c. perform the LU Decomposition
197 | for j in range(n):
198 |
199 | for i in range(j+1):
200 | c = U[:,j]@L[i,:]
201 | U[i][j] = A[i][j] - c
202 |
203 | for i in range(j, n):
204 | c = U[:j,j]@L[i,:j]
205 | L[i][j] = (A[i][j] - c) / U[j][j]
206 |
207 | return L,U
208 |
209 | def construct_sympy_matrix(positions,name='a'):
210 | """ construct sympy matrix with non-zero elements in positions
211 |
212 | Args:
213 |
214 | Positions (list): list of positions in strings, e.g. ['11','31']
215 |
216 | Returns:
217 |
218 | mat (sympy.matrix): Sympy Matrix
219 |
220 | """
221 |
222 | # a. dictionary of element with position as key and a_position as value
223 | entries = {f'{ij}':sm.symbols(f'{name}_{ij}') for ij in positions}
224 |
225 | # b. function for creating element or zero
226 | add = lambda x: entries[x] if x in entries else 0
227 |
228 | # c. create matrix
229 | mat_as_list = [[add(f'{1+i}{1+j}') for j in range(3)] for i in range(3)]
230 | mat = sm.Matrix(mat_as_list)
231 |
232 | return mat
233 |
234 | def fill_sympy_matrix(A_sm,A,name='a'):
235 |
236 | n,m = A.shape
237 |
238 | # a. make all substitution
239 | A_sm_copy = A_sm
240 | for i in range(n):
241 | for j in range(m):
242 | if not A[i,j] == 0:
243 | A_sm_copy = A_sm_copy.subs(f'{name}_{1+i}{1+j}',A[i,j])
244 |
245 | # b. lambdify with no inputs
246 | f = sm.lambdify((),A_sm_copy)
247 |
248 | # c. return filled matrix
249 | return f()
250 |
251 |
252 |
--------------------------------------------------------------------------------
/5 - Outroduction/PS7/ConsumerModel.py:
--------------------------------------------------------------------------------
1 | # Import packages (just the ones you need)
2 | import numpy as np
3 | import matplotlib.pyplot as plt
4 | from types import SimpleNamespace
5 | from scipy import optimize
6 | plt.rcParams.update({"axes.grid":True,"grid.color":"black","grid.alpha":"0.25","grid.linestyle":"--"})
7 | plt.rcParams.update({'font.size': 14})
8 | from scipy import interpolate
9 |
10 |
11 | # Define the model
12 | class ConsumerModelClass():
13 |
14 | def __init__(self, **kwargs):
15 | '''
16 | Initialize the model with default parameters
17 | kwargs allow any parameter in the par namespace to be overridden by the user
18 | '''
19 |
20 | self.par = par = SimpleNamespace() # Create a namespace object for parameters
21 | self.sol = sol = SimpleNamespace() # Create a namespace object for solution results
22 | self.sim = sim = SimpleNamespace() # Create a namespace object for simulation results (not always neccesary)
23 |
24 | # Set default parameters
25 | self.setup()
26 |
27 | # Update parameters with user input
28 | for key, value in kwargs.items():
29 | setattr(par, key, value)
30 |
31 | # Allocate arrays simulation (if needed)
32 | self.allocate()
33 |
34 |
35 | def setup(self):
36 | '''
37 | Set default parameters
38 | '''
39 | par = self.par
40 |
41 | # Model parameters
42 | par.rho = 8
43 | par.kappa = 0.5
44 | par.nu = 0.1
45 | par.r = 0.04
46 | par.beta = 0.94
47 | par.Delta = 0.5
48 |
49 |
50 | # Helpful variables
51 | par.approx0 = 1e-8
52 |
53 | par.vecN = 500
54 |
55 |
56 |
57 | def allocate(self):
58 | '''
59 | Allocate arrays for simulation
60 | '''
61 |
62 | par = self.par
63 | sol = self.sol
64 |
65 | # solution vecs
66 | vecs = ['m1','m2','c1','c2','v1','v2']
67 | for vec in vecs:
68 | setattr(sol,f'{vec}_vec',np.empty(par.vecN))
69 |
70 | sol.m2_vec[:] = np.linspace(par.approx0,5,par.vecN)
71 | sol.m1_vec[:] = np.linspace(par.approx0,4,par.vecN)
72 |
73 | def utility(self, c):
74 | par = self.par
75 | return c**(1-par.rho)/(1-par.rho)
76 |
77 | def bequest(self, m, c):
78 | par = self.par
79 | return par.nu*(m-c+par.kappa)**(1-par.rho)/(1-par.rho)
80 |
81 | def v2(self, c2, m2):
82 | return self.utility(c2) + self.bequest(m2, c2)
83 |
84 | def v1(self, c1, m1):
85 | par = self.par
86 | sol = self.sol
87 |
88 | # a. v2 value, if low income
89 | m2_low = (1+par.r)*(m1-c1) + 1-par.Delta
90 | v2_low = sol.v2_interp([m2_low])[0]
91 |
92 | # b. v2 value, if high income
93 | m2_high = (1+par.r)*(m1-c1) + 1+par.Delta
94 | v2_high = sol.v2_interp([m2_high])[0]
95 |
96 | # c. expected v2 value
97 | v2 = 0.5*v2_low + 0.5*v2_high
98 |
99 | # d. total value
100 | return self.utility(c1) + par.beta*v2
101 |
102 | def v1_alt(self,c1,m1):
103 | par = self.par
104 | sol = self.sol
105 |
106 | probs = np.array([0.1,0.4,0.4,0.1])
107 | y2s = np.array([1-np.sqrt(par.Delta),1-par.Delta,1+par.Delta,1+np.sqrt(par.Delta)])
108 |
109 | #
110 | m2_vec = (1+par.r)*(m1-c1) +y2s
111 | v2s = sol.v2_interp(m2_vec)
112 |
113 | # expected v2 value
114 | v2 = probs@v2s
115 |
116 | # total value
117 | return self.utility(c1) + par.beta*v2
118 |
119 |
120 | def solve_period_2(self):
121 | par = self.par
122 | sol = self.sol
123 |
124 | # b. solve for each m2 in grid
125 | for i,m2 in enumerate(sol.m2_vec):
126 |
127 | # i. objective
128 | obj = lambda x: -self.v2(x[0],m2)
129 |
130 | # ii. initial value (consume half)
131 | x0 = m2/2
132 |
133 | # iii. optimizer
134 | result = optimize.minimize(obj,[x0],method='L-BFGS-B',bounds=((par.approx0/100,m2),))
135 |
136 | # iv. save
137 | sol.v2_vec[i] = -result.fun
138 | sol.c2_vec[i] = result.x[0]
139 |
140 |
141 |
142 | def solve_period_1(self,v1_type='original'):
143 | par = self.par
144 | sol = self.sol
145 |
146 | # b. solve for each m1 in grid
147 | for i,m1 in enumerate(sol.m1_vec):
148 |
149 | # i. objective
150 | if v1_type == 'original':
151 | obj = lambda x: -self.v1(x[0],m1)
152 | elif v1_type == 'alt':
153 | obj = lambda x: -self.v1_alt(x[0],m1)
154 |
155 | # ii. initial guess (consume half)
156 | x0 = m1/2
157 |
158 | # iii. optimize
159 | result = optimize.minimize(obj,[x0],method='L-BFGS-B',bounds=((par.approx0/100,m1),))
160 |
161 | # iv. save
162 | sol.v1_vec[i] = -result.fun
163 | sol.c1_vec[i] = result.x[0]
164 |
165 |
166 | def solve(self,v1_type='original'):
167 | par = self.par
168 | sol = self.sol
169 |
170 | # a. solve period 2
171 | self.solve_period_2()
172 |
173 | # b. construct interpolator
174 | sol.v2_interp = interpolate.RegularGridInterpolator((sol.m2_vec,), sol.v2_vec,
175 | bounds_error=False,fill_value=None)
176 | # c. solve period 1
177 | self.solve_period_1(v1_type=v1_type)
178 |
179 |
180 | def plot_c1(self,add_alt=False):
181 | par = self.par
182 | sol = self.sol
183 |
184 | fig,ax = plt.subplots()
185 | ax.plot(sol.m1_vec,sol.c1_vec,label='Original')
186 | ax.set_xlabel('$m_1$')
187 | ax.set_ylabel('$c_1$')
188 | ax.set_title('Consumption in period 1')
189 |
190 | if add_alt:
191 | # v2_interp is the same for this new utility function in period 1
192 | self.solve_period_1(v1_type='alt')
193 | ax.plot(sol.m1_vec,sol.c1_vec,label='Alternative utility function')
194 | ax.legend()
195 | plt.show()
--------------------------------------------------------------------------------
/5 - Outroduction/PS7/consumper_:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NumEconCopenhagen/IntroProg-lectures/bfd33808dbfc7bf3e60851b608c50d1bb0fbb5c2/5 - Outroduction/PS7/consumper_
--------------------------------------------------------------------------------
/5 - Outroduction/PS7/contourplot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NumEconCopenhagen/IntroProg-lectures/bfd33808dbfc7bf3e60851b608c50d1bb0fbb5c2/5 - Outroduction/PS7/contourplot.png
--------------------------------------------------------------------------------
/5 - Outroduction/PS7/surfaceplot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NumEconCopenhagen/IntroProg-lectures/bfd33808dbfc7bf3e60851b608c50d1bb0fbb5c2/5 - Outroduction/PS7/surfaceplot.png
--------------------------------------------------------------------------------
/5 - Outroduction/Problem_1.py:
--------------------------------------------------------------------------------
1 |
2 | # Import packages (just the ones you need)
3 | import numpy as np
4 | import matplotlib.pyplot as plt
5 | from types import SimpleNamespace
6 | from scipy import optimize
7 | plt.rcParams.update({"axes.grid":True,"grid.color":"black","grid.alpha":"0.25","grid.linestyle":"--"})
8 | plt.rcParams.update({'font.size': 14})
9 |
10 |
11 | # Define the model
12 | class GovModel():
13 |
14 | def __init__(self, **kwargs):
15 | '''
16 | Initialize the model with default parameters
17 | kwargs allow any parameter in the par namespace to be overridden by the user
18 | '''
19 |
20 | self.par = par = SimpleNamespace() # Create a namespace object for parameters
21 | self.sol_cb = sol_cb = SimpleNamespace() # Create a namespace object for solution results
22 | self.sol_ces = sol_ces = SimpleNamespace() # Create a namespace object for solution results
23 |
24 | # Set default parameters
25 | self.setup()
26 |
27 | # Update parameters with user input
28 | for key, value in kwargs.items():
29 | setattr(par, key, value)
30 |
31 |
32 | def setup(self):
33 | '''
34 | Set default parameters
35 | '''
36 | par = self.par
37 |
38 | # Model parameters
39 | par.alpha = 0.5
40 | par.kappa =1.0
41 | par.nu = 1/(2*16**2)
42 | par.w = 1.0
43 | par.tau = 0.3
44 |
45 | par.sigma = 1.001
46 | par.rho = 1.001
47 | par.varepsilon = 1.0
48 |
49 | par.zero_tol = 1e-8
50 |
51 |
52 |
53 | def u_cb(self,L, G):
54 | '''
55 | Cobb Douglass utility function
56 | '''
57 | par = self.par
58 | C = par.kappa + (1-par.tau)*par.w*L
59 | C_bounded = np.fmax(C,par.zero_tol)
60 | G_bounded = np.fmax(G,par.zero_tol)
61 |
62 | return np.log( (C_bounded**par.alpha) * G_bounded**(1-par.alpha) ) - par.nu*L**2/2
63 |
64 |
65 | def L_star_analytical(self,G=1):
66 | '''
67 | Analytical solution for optimal labor supply
68 | '''
69 | par = self.par
70 | sol_cb = self.sol_cb
71 |
72 | wtilde = par.w*(1-par.tau)
73 |
74 | sol_cb.L_analytical = (-par.kappa+ np.sqrt(par.kappa**2+ 4*par.alpha/par.nu*wtilde**2)) /(2*wtilde)
75 |
76 | def u_ces(self,L,G):
77 | '''
78 | Utility function
79 | '''
80 | par = self.par
81 | C = par.kappa + (1-par.tau)*par.w*L
82 | C_bounded = np.fmax(C,par.zero_tol)
83 | G_bounded = np.fmax(G,par.zero_tol)
84 |
85 | term1 = (par.alpha * C_bounded**((par.sigma-1)/par.sigma) + (1-par.alpha) * G_bounded**((par.sigma-1)/par.sigma))**(par.sigma/(par.sigma-1))
86 | term2 = par.nu * L**(1+par.varepsilon) / (1+par.varepsilon)
87 |
88 | return (term1**(1-par.rho) -1) / (1-par.rho) - term2
89 |
--------------------------------------------------------------------------------
/5 - Outroduction/Problem_1_video.py:
--------------------------------------------------------------------------------
1 |
2 | # Import packages (just the ones you need)
3 | import numpy as np
4 | import matplotlib.pyplot as plt
5 | from types import SimpleNamespace
6 | from scipy import optimize
7 | plt.rcParams.update({"axes.grid":True,"grid.color":"black","grid.alpha":"0.25","grid.linestyle":"--"})
8 | plt.rcParams.update({'font.size': 14})
9 |
10 |
11 | # Define the model
12 | class GovModel():
13 |
14 | def __init__(self, **kwargs):
15 | '''
16 | Initialize the model with default parameters
17 | kwargs allow any parameter in the par namespace to be overridden by the user
18 | '''
19 |
20 | self.par = par = SimpleNamespace() # Create a namespace object for parameters
21 | self.sol_cb = sol_cb = SimpleNamespace() # Create a namespace object for solution results
22 | self.sol_ces = sol_ces = SimpleNamespace() # Create a namespace object for solution results
23 |
24 | # Set default parameters
25 | self.setup()
26 |
27 | # Update parameters with user input
28 | for key, value in kwargs.items():
29 | setattr(par, key, value)
30 |
31 |
32 | def setup(self):
33 | '''
34 | Set default parameters
35 | '''
36 | par = self.par
37 |
38 | # Model parameters
39 | par.alpha = 0.5
40 | par.kappa =1.0
41 | par.nu = 1/(2*16**2)
42 | par.w = 1.0
43 | par.tau = 0.3
44 |
45 | par.sigma = 1.001
46 | par.rho = 1.001
47 | par.varepsilon = 1.0
48 |
49 | par.zero_tol = 1e-8
50 |
51 |
52 |
53 | def u_cb(self,L, G):
54 | '''
55 | Cobb Douglass utility function
56 | '''
57 | par = self.par
58 | C = par.kappa + (1-par.tau)*par.w*L
59 | C_bounded = np.fmax(C,par.zero_tol)
60 | G_bounded = np.fmax(G,par.zero_tol)
61 |
62 | return np.log( (C_bounded**par.alpha) * G_bounded**(1-par.alpha) ) - par.nu*L**2/2
63 |
64 |
65 | def L_star_analytical(self,G=1):
66 | '''
67 | Analytical solution for optimal labor supply
68 | '''
69 | par = self.par
70 | sol_cb = self.sol_cb
71 |
72 | wtilde = par.w*(1-par.tau)
73 |
74 | sol_cb.L_analytical = (-par.kappa+ np.sqrt(par.kappa**2+ 4*par.alpha/par.nu*wtilde**2)) /(2*wtilde)
75 |
76 | def L_star_numerical(self,G=1,ufunc='cb'):
77 | par = self.par
78 | sol = getattr(self,'sol_'+ufunc)
79 |
80 | # Define objective function
81 | u = getattr(self,'u_'+ufunc)
82 |
83 | obj = lambda L: -u(L,G)
84 |
85 | # Optimize
86 | sol.L_star = optimize.minimize_scalar(obj,bounds=(0,24)).x
87 |
88 |
89 | def u_ces(self,L,G):
90 | '''
91 | Utility function
92 | '''
93 | par = self.par
94 | C = par.kappa + (1-par.tau)*par.w*L
95 | C_bounded = np.fmax(C,par.zero_tol)
96 | G_bounded = np.fmax(G,par.zero_tol)
97 |
98 | term1 = (par.alpha * C_bounded**((par.sigma-1)/par.sigma) + (1-par.alpha) * G_bounded**((par.sigma-1)/par.sigma))**(par.sigma/(par.sigma-1))
99 | term2 = par.nu * L**(1+par.varepsilon) / (1+par.varepsilon)
100 |
101 | return (term1**(1-par.rho) -1) / (1-par.rho) - term2
102 |
103 | def utility_budget_cb(self):
104 | '''
105 | Compute utility at optimal labor supply given that the budget is balanced
106 | '''
107 | par = self.par
108 | sol = self.sol_cb
109 |
110 |
111 | self.L_star_analytical()
112 | sol.G = par.tau*par.w*sol.L_analytical
113 | sol.U_star =self.u_cb(sol.L_analytical,sol.G)
114 |
115 | def tau_star_cb(self):
116 | '''
117 | Compute optimal tax rate for cobb douglas
118 | '''
119 | par = self.par
120 | sol = self.sol_cb
121 |
122 | def obj(tau):
123 | par.tau = tau
124 | self.utility_budget_cb()
125 | return -sol.U_star
126 |
127 | sol.tau_star = optimize.minimize_scalar(obj,bounds=(0,1)).x
128 |
129 |
130 | def find_G_ces(self,tau=0.3,printit=True):
131 | '''
132 | Compute the G that balances the budget in the ces case
133 | '''
134 | par = self.par
135 | sol = self.sol_ces
136 |
137 | par.tau = tau
138 | def obj(G):
139 | self.L_star_numerical(G,ufunc='ces')
140 |
141 | return G - par.tau * par.w * sol.L_star
142 |
143 | sol.G_star = optimize.root_scalar(obj,bracket=[0,tau*par.w*24 ]).root
144 | if printit:
145 | print(f'G_star = {sol.G_star:.2f}')
146 |
147 | def find_tau_star_ces(self):
148 | '''
149 | Compute optimal tax rate for CES
150 | '''
151 | par = self.par
152 | sol = self.sol_ces
153 |
154 | def obj(tau):
155 | par.tau = tau
156 | self.find_G_ces(tau,printit=False)
157 | U = self.u_ces(sol.L_star,sol.G_star)
158 | return -U
159 |
160 | sol.tau_star = optimize.minimize_scalar(obj,bounds=(0,1)).x
161 | print(f'tau_star = {sol.tau_star:.2f}')
162 |
163 | def plot_utiltiy(self,G=1,ufunc='cb'):
164 | '''
165 | Plot utility as a function of labor supply
166 | '''
167 | par = self.par
168 | sol = self.sol_cb
169 |
170 | # Create figure
171 | fig = plt.figure()
172 | ax = fig.add_subplot(1,1,1)
173 |
174 | # Create grid
175 | L_vec = np.linspace(10, 20, 100)
176 |
177 | # Compute utility
178 | u_vec = self.u_ces(L_vec, G)
179 |
180 | # Analytical solution
181 | self.L_star_analytical(G)
182 |
183 | # Numerical solution
184 | self.L_star_numerical(G,ufunc=ufunc)
185 |
186 | # Plot
187 | ax.plot(L_vec, u_vec)
188 | ax.axvline(sol.L_analytical, color='red', label='Analytical solution')
189 | ax.axvline(sol.L_star, color='green', linestyle='--', label='Numerical solution')
190 | ax.set_xlabel('Labor supply')
191 | ax.set_ylabel('Utility')
192 | ax.legend()
193 | ax.grid(True)
194 | plt.show()
195 |
196 |
197 |
198 | def plot_L_star_across_w(self,G=1):
199 | '''
200 | Plot optimal labor supply as a function of w
201 | '''
202 | par = self.par
203 | sol = self.sol_cb
204 |
205 | # Create figure
206 | fig = plt.figure()
207 | ax = fig.add_subplot(1,1,1)
208 |
209 | # Create grid
210 | w_vec = np.linspace(0.5, 3, 100)
211 |
212 |
213 | w_org = par.w
214 | # Compute optimal labor supply
215 | L_star_vec = np.empty_like(w_vec)
216 | for i, w in enumerate(w_vec):
217 | par.w = w
218 | self.L_star_analytical(G)
219 | L_star_vec[i] = self.sol_cb.L_analytical
220 |
221 | # Reset w
222 | par.w = w_org
223 |
224 | # Plot
225 | ax.plot(w_vec, L_star_vec)
226 | ax.set_xlabel('w')
227 | ax.set_ylabel('Labor supply')
228 | ax.grid(True)
229 | plt.show()
230 |
231 |
232 | def plot_model_across_tau_cb(self):
233 | '''
234 | Plot solution across
235 | '''
236 |
237 | par = self.par
238 | sol = self.sol_cb
239 |
240 | tau_grid = np.linspace(0.1,0.9, 100)
241 |
242 | L_star_vec = np.empty_like(tau_grid)
243 | G_star_vec = np.empty_like(tau_grid)
244 | U_star_vec = np.empty_like(tau_grid)
245 |
246 |
247 | org_tau = par.tau
248 | for i, tau in enumerate(tau_grid):
249 | par.tau = tau
250 | self.utility_budget_cb()
251 |
252 | L_star_vec[i] = sol.L_analytical
253 | G_star_vec[i] = sol.G
254 | U_star_vec[i] = sol.U_star
255 |
256 | # reset tau
257 | par.tau = org_tau
258 |
259 | fig,axes = plt.subplots(3,1,figsize=(10,10))
260 | axes[0].plot(tau_grid,L_star_vec)
261 | axes[0].set_ylabel('Labor supply')
262 | axes[0].set_title('Labor supply')
263 |
264 |
265 | axes[1].plot(tau_grid,G_star_vec)
266 | axes[1].set_ylabel('Government spending')
267 | axes[1].set_title('Government spending')
268 |
269 | axes[2].plot(tau_grid,U_star_vec)
270 | axes[2].set_ylabel('Utility')
271 | axes[2].set_title('Utility')
272 | fig.tight_layout()
273 | plt.show()
274 |
--------------------------------------------------------------------------------
/5 - Outroduction/figs/ku_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NumEconCopenhagen/IntroProg-lectures/bfd33808dbfc7bf3e60851b608c50d1bb0fbb5c2/5 - Outroduction/figs/ku_logo.png
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 NumEconCopenhagen
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [Visit course web-page](https://sites.google.com/view/numeconcph-introprog/home)
--------------------------------------------------------------------------------
/admin/1. GroupsOnGithub.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "attachments": {},
5 | "cell_type": "markdown",
6 | "id": "bc91d7fb-e3d5-4c13-a629-4b55d9ff2ac4",
7 | "metadata": {},
8 | "source": [
9 | "# Get members of groups based on Github repositories\n",
10 | "Used for finding out exercise class membership "
11 | ]
12 | },
13 | {
14 | "cell_type": "code",
15 | "execution_count": null,
16 | "id": "a9aa84c5-e3be-41ae-b4aa-3def3bd47f0c",
17 | "metadata": {},
18 | "outputs": [],
19 | "source": [
20 | "from github import Github # pip install PyGithub\n",
21 | "import sys\n",
22 | "import datetime\n",
23 | "import pandas as pd\n",
24 | "import numpy as np\n",
25 | "import random\n",
26 | "from collections import Counter"
27 | ]
28 | },
29 | {
30 | "cell_type": "code",
31 | "execution_count": null,
32 | "id": "df74039f",
33 | "metadata": {},
34 | "outputs": [],
35 | "source": [
36 | "%load_ext autoreload\n",
37 | "%autoreload 2 \n",
38 | "from groups_utils import load_repos"
39 | ]
40 | },
41 | {
42 | "attachments": {},
43 | "cell_type": "markdown",
44 | "id": "0f19d918-7c7a-492e-8b73-6fa837277677",
45 | "metadata": {},
46 | "source": [
47 | "### Load all repositories in this year's class room"
48 | ]
49 | },
50 | {
51 | "cell_type": "code",
52 | "execution_count": null,
53 | "id": "8838852a-b033-476c-827f-5e6485cceec5",
54 | "metadata": {},
55 | "outputs": [],
56 | "source": [
57 | "class_name, all_repos, current_class, disregard_repos = load_repos(print_groups=True,check_year=True)"
58 | ]
59 | },
60 | {
61 | "attachments": {},
62 | "cell_type": "markdown",
63 | "id": "98cc5c4e",
64 | "metadata": {},
65 | "source": [
66 | "### Get members"
67 | ]
68 | },
69 | {
70 | "cell_type": "code",
71 | "execution_count": null,
72 | "id": "bc7244d4-f84c-45e9-8f05-fa2370d830cb",
73 | "metadata": {},
74 | "outputs": [],
75 | "source": [
76 | "class_repos = []\n",
77 | "groups = {}\n",
78 | "admins = [\"AndersMunkN\", \"JeppeDruedahl\", \"ChristianLangholzCarstensen\",'AskerNC']\n",
79 | "students = []\n",
80 | "\n",
81 | "for repo in all_repos:\n",
82 | "\n",
83 | " if repo.name not in current_class: continue\n",
84 | " \n",
85 | " # get the set of collaborators for a repo - remove organization admins\n",
86 | " collaborators = repo.get_collaborators().get_page(0)\n",
87 | " members = [c.login for c in collaborators if c.login not in admins] \n",
88 | " \n",
89 | " # add members to dictionary and to list of all students\n",
90 | " grp_name = repo.name.removeprefix(class_name+\"-\")\n",
91 | "\n",
92 | " if len(members) == 0:\n",
93 | " # Make sure these are abbandoned/deleted groups\n",
94 | " print(f\"No members in group: {grp_name}\")\n",
95 | " else:\n",
96 | " groups[grp_name] = members\n",
97 | " \n",
98 | " students.extend(members)\n"
99 | ]
100 | },
101 | {
102 | "cell_type": "code",
103 | "execution_count": null,
104 | "id": "6ab33eb0",
105 | "metadata": {},
106 | "outputs": [],
107 | "source": [
108 | "print('Number of groups:', len(groups))\n",
109 | "print('Number of students:', len(students))"
110 | ]
111 | },
112 | {
113 | "attachments": {},
114 | "cell_type": "markdown",
115 | "id": "925e11a1",
116 | "metadata": {},
117 | "source": [
118 | "### To Excel"
119 | ]
120 | },
121 | {
122 | "cell_type": "code",
123 | "execution_count": null,
124 | "id": "e37dc43c-9983-4f85-93c3-69720ec24a7a",
125 | "metadata": {},
126 | "outputs": [],
127 | "source": [
128 | "gd = {k:pd.Series(v,dtype='str') for k,v in groups.items()}\n",
129 | "\n",
130 | "groups_df = pd.DataFrame(gd) \n",
131 | "groups_df = groups_df.T\n",
132 | "groups_df.rename(columns = {0:'Member1',1:'Member2',2:'Member3'}, inplace=True)\n",
133 | "groups_df.fillna('',inplace=True)\n",
134 | "\n",
135 | "groups_df.sort_index(inplace=True)\n",
136 | "\n",
137 | "for col in ['Exercise class','Comments','TA']:\n",
138 | " groups_df[col] = ''"
139 | ]
140 | },
141 | {
142 | "cell_type": "code",
143 | "execution_count": null,
144 | "id": "c028a007-41a6-4452-8015-430932d78e3c",
145 | "metadata": {},
146 | "outputs": [],
147 | "source": [
148 | "groups_df.reset_index(inplace=True)\n",
149 | "groups_df.rename(columns={'index':'Group'}, inplace=True)\n",
150 | "groups_df.head()"
151 | ]
152 | },
153 | {
154 | "cell_type": "code",
155 | "execution_count": null,
156 | "id": "82ed10a2-5ebe-4488-be2e-cf50742c1239",
157 | "metadata": {
158 | "tags": []
159 | },
160 | "outputs": [],
161 | "source": [
162 | "groups_df.to_excel('Groups.xlsx',index=False)"
163 | ]
164 | }
165 | ],
166 | "metadata": {
167 | "kernelspec": {
168 | "display_name": "base",
169 | "language": "python",
170 | "name": "python3"
171 | },
172 | "language_info": {
173 | "codemirror_mode": {
174 | "name": "ipython",
175 | "version": 3
176 | },
177 | "file_extension": ".py",
178 | "mimetype": "text/x-python",
179 | "name": "python",
180 | "nbconvert_exporter": "python",
181 | "pygments_lexer": "ipython3",
182 | "version": "3.11.5"
183 | },
184 | "vscode": {
185 | "interpreter": {
186 | "hash": "47ef90cdf3004d3f859f1fb202523c65c07ba7c22eefd261b181f4744e2d0403"
187 | }
188 | }
189 | },
190 | "nbformat": 4,
191 | "nbformat_minor": 5
192 | }
193 |
--------------------------------------------------------------------------------
/admin/2. PeerFeedbackAssignments.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "attachments": {},
5 | "cell_type": "markdown",
6 | "id": "7ac22349",
7 | "metadata": {},
8 | "source": [
9 | "# Assignment of peer feedback\n",
10 | "\n",
11 | "This notebook contains the code used to determine which two other teams each team must give feed back to in the data project"
12 | ]
13 | },
14 | {
15 | "cell_type": "code",
16 | "execution_count": null,
17 | "id": "5410363f",
18 | "metadata": {},
19 | "outputs": [],
20 | "source": [
21 | "from github import Github # pip install PyGithub\n",
22 | "import sys\n",
23 | "import datetime\n",
24 | "import pandas as pd\n",
25 | "import numpy as np\n",
26 | "import random\n",
27 | "from collections import Counter"
28 | ]
29 | },
30 | {
31 | "cell_type": "code",
32 | "execution_count": null,
33 | "id": "b90deed2",
34 | "metadata": {},
35 | "outputs": [],
36 | "source": [
37 | "%load_ext autoreload\n",
38 | "%autoreload 2 \n",
39 | "from groups_utils import load_repos"
40 | ]
41 | },
42 | {
43 | "attachments": {},
44 | "cell_type": "markdown",
45 | "id": "0f19d918-7c7a-492e-8b73-6fa837277677",
46 | "metadata": {},
47 | "source": [
48 | "### Load all repositories in this year's class room"
49 | ]
50 | },
51 | {
52 | "cell_type": "code",
53 | "execution_count": null,
54 | "id": "d669497c",
55 | "metadata": {},
56 | "outputs": [],
57 | "source": [
58 | "class_name, all_repos, current_class, disregard_repos = load_repos()"
59 | ]
60 | },
61 | {
62 | "attachments": {},
63 | "cell_type": "markdown",
64 | "id": "2dfbc175",
65 | "metadata": {},
66 | "source": [
67 | "### All teams"
68 | ]
69 | },
70 | {
71 | "cell_type": "code",
72 | "execution_count": null,
73 | "id": "0ceff6d1",
74 | "metadata": {},
75 | "outputs": [],
76 | "source": [
77 | "teams = set()\n",
78 | "i = 1\n",
79 | "for repo in all_repos:\n",
80 | " if repo.name not in current_class: continue\n",
81 | " if repo.name[14:] in disregard_repos: \n",
82 | " print(repo.name)\n",
83 | " continue \n",
84 | " teams.add(repo.name[14:])\n",
85 | " i += 1"
86 | ]
87 | },
88 | {
89 | "attachments": {},
90 | "cell_type": "markdown",
91 | "id": "863e231f",
92 | "metadata": {},
93 | "source": [
94 | "### Excel sheets"
95 | ]
96 | },
97 | {
98 | "cell_type": "code",
99 | "execution_count": null,
100 | "id": "5153f517",
101 | "metadata": {},
102 | "outputs": [],
103 | "source": [
104 | "random.seed(123)\n",
105 | "for k in range(10):\n",
106 | "\n",
107 | " try:\n",
108 | "\n",
109 | " for projectname in ['inauguralproject', 'dataproject', 'modelproject']:\n",
110 | "\n",
111 | " # a. create data frame\n",
112 | " teams_df = pd.DataFrame(data=teams,columns=['team'])\n",
113 | " teams_df.sort_values(by='team',inplace=True)\n",
114 | " teams_df.reset_index(drop=True, inplace=True)\n",
115 | " teams_df['peer_group_1'] = ''\n",
116 | " teams_df['peer_group_2'] = ''\n",
117 | "\n",
118 | " # b. assign peers to teams\n",
119 | " N = len(teams_df.team.values)\n",
120 | " counter = [0 for _ in range(N)]\n",
121 | " assigned = []\n",
122 | "\n",
123 | " # for each team, loop over teams that have not already been assigned\n",
124 | " for i,_ in enumerate(teams_df.team):\n",
125 | " pop = [t for t in range(N) if (t != i) & (t not in assigned)]\n",
126 | " peers = random.sample(pop, 2) \n",
127 | " for c in [0,1]:\n",
128 | " teams_df.iloc[i, 1 + c] = teams_df.team[peers[c]]\n",
129 | " counter[peers[c]] += 1\n",
130 | " if counter[peers[c]] == 2:\n",
131 | " assigned.append(peers[c])\n",
132 | "\n",
133 | " teams_df.to_excel(f'PeerAssignment_{projectname}.xlsx',index=False)\n",
134 | "\n",
135 | " print(f'attempt {k+1} was successful')\n",
136 | " break\n",
137 | "\n",
138 | " except Exception as e:\n",
139 | "\n",
140 | " print(f'attempt {k+1} failed: {e}')"
141 | ]
142 | },
143 | {
144 | "cell_type": "code",
145 | "execution_count": null,
146 | "id": "cfde56af",
147 | "metadata": {},
148 | "outputs": [],
149 | "source": []
150 | }
151 | ],
152 | "metadata": {
153 | "kernelspec": {
154 | "display_name": "base",
155 | "language": "python",
156 | "name": "python3"
157 | },
158 | "language_info": {
159 | "codemirror_mode": {
160 | "name": "ipython",
161 | "version": 3
162 | },
163 | "file_extension": ".py",
164 | "mimetype": "text/x-python",
165 | "name": "python",
166 | "nbconvert_exporter": "python",
167 | "pygments_lexer": "ipython3",
168 | "version": "3.11.5"
169 | },
170 | "toc-autonumbering": false,
171 | "vscode": {
172 | "interpreter": {
173 | "hash": "47ef90cdf3004d3f859f1fb202523c65c07ba7c22eefd261b181f4744e2d0403"
174 | }
175 | }
176 | },
177 | "nbformat": 4,
178 | "nbformat_minor": 5
179 | }
180 |
--------------------------------------------------------------------------------
/admin/3. RepoVisibility.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "attachments": {},
5 | "cell_type": "markdown",
6 | "id": "4a9fd3f3-ec08-43bc-8934-11d4475814c9",
7 | "metadata": {},
8 | "source": [
9 | "# Change visibility status of projects"
10 | ]
11 | },
12 | {
13 | "cell_type": "code",
14 | "execution_count": null,
15 | "id": "b4a69b89-2eee-436e-857e-58966be3b7bc",
16 | "metadata": {},
17 | "outputs": [],
18 | "source": [
19 | "#%pip install PyGithub"
20 | ]
21 | },
22 | {
23 | "cell_type": "code",
24 | "execution_count": null,
25 | "id": "f9da979b-0fb8-4bf5-bc2d-2852aa3a1385",
26 | "metadata": {},
27 | "outputs": [],
28 | "source": [
29 | "from github import Github \n",
30 | "import sys\n",
31 | "import datetime\n",
32 | "import pandas as pd\n",
33 | "import numpy as np\n",
34 | "import random\n",
35 | "from collections import Counter"
36 | ]
37 | },
38 | {
39 | "cell_type": "code",
40 | "execution_count": null,
41 | "id": "effd94fb",
42 | "metadata": {},
43 | "outputs": [],
44 | "source": [
45 | "%load_ext autoreload\n",
46 | "%autoreload 2 \n",
47 | "from groups_utils import load_repos"
48 | ]
49 | },
50 | {
51 | "attachments": {},
52 | "cell_type": "markdown",
53 | "id": "66bda1ab-3722-4c45-8850-8367088c6453",
54 | "metadata": {},
55 | "source": [
56 | "### Load all repositories in this year's class room"
57 | ]
58 | },
59 | {
60 | "cell_type": "code",
61 | "execution_count": null,
62 | "id": "cc1f2223-bbd2-4bc8-8219-8d1244d12d37",
63 | "metadata": {},
64 | "outputs": [],
65 | "source": [
66 | "class_name, all_repos, current_class, disregard_repos = load_repos()"
67 | ]
68 | },
69 | {
70 | "attachments": {},
71 | "cell_type": "markdown",
72 | "id": "05db6b56-075a-4a5d-9425-1786215b1a0d",
73 | "metadata": {},
74 | "source": [
75 | "### Set visibility of all repositories"
76 | ]
77 | },
78 | {
79 | "cell_type": "code",
80 | "execution_count": null,
81 | "id": "61c04db4-dcfb-4f14-b5dc-c470520db66b",
82 | "metadata": {},
83 | "outputs": [],
84 | "source": [
85 | "# change\n",
86 | "is_private = True\n",
87 | "\n",
88 | "# set privacy status\n",
89 | "for repo in all_repos:\n",
90 | "\n",
91 | " if repo.name not in current_class: continue\n",
92 | " \n",
93 | " # update status \n",
94 | " repo.edit(private = is_private)"
95 | ]
96 | },
97 | {
98 | "attachments": {},
99 | "cell_type": "markdown",
100 | "id": "51eef9c4-abc7-4db1-aa03-e324dc1a9d03",
101 | "metadata": {},
102 | "source": [
103 | "### Check out visibility"
104 | ]
105 | },
106 | {
107 | "cell_type": "code",
108 | "execution_count": null,
109 | "id": "97f0a3f1-9537-42ca-a15b-f6af68391469",
110 | "metadata": {},
111 | "outputs": [],
112 | "source": [
113 | "for repo in all_repos:\n",
114 | "\n",
115 | " if repo.name not in current_class: continue\n",
116 | " \n",
117 | " if repo.private:\n",
118 | " s = \"private\"\n",
119 | " else: \n",
120 | " s = \"public \"\n",
121 | " \n",
122 | " print(s + \": \" + repo.name.removeprefix(class_name+\"-\"))"
123 | ]
124 | },
125 | {
126 | "cell_type": "code",
127 | "execution_count": null,
128 | "id": "dace07d9",
129 | "metadata": {},
130 | "outputs": [],
131 | "source": []
132 | }
133 | ],
134 | "metadata": {
135 | "kernelspec": {
136 | "display_name": "base",
137 | "language": "python",
138 | "name": "python3"
139 | },
140 | "language_info": {
141 | "codemirror_mode": {
142 | "name": "ipython",
143 | "version": 3
144 | },
145 | "file_extension": ".py",
146 | "mimetype": "text/x-python",
147 | "name": "python",
148 | "nbconvert_exporter": "python",
149 | "pygments_lexer": "ipython3",
150 | "version": "3.11.5"
151 | },
152 | "vscode": {
153 | "interpreter": {
154 | "hash": "47ef90cdf3004d3f859f1fb202523c65c07ba7c22eefd261b181f4744e2d0403"
155 | }
156 | }
157 | },
158 | "nbformat": 4,
159 | "nbformat_minor": 5
160 | }
161 |
--------------------------------------------------------------------------------
/admin/ReadMe.md:
--------------------------------------------------------------------------------
1 | # Prepare
2 |
3 | 1. Ensure you are _owner_ of https://github.com/NumEconCopenhagen (can be changed under `People`)
4 |
5 | - Generate personal access token: https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens
6 | 2. Create assignment `projects-20xx` on https://classroom.github.com/
7 | (If you do not have access to a NumEconCopenhagen classroom upon pressing the link, you can create a new one, from which the assignment can be created)
8 |
9 | team name: students-20xx
10 | visiblity: public
11 | admin access: yes
12 | template: NumEconCopenhagen/IntroProg-example
13 | 3. Use `1. GroupsOnGithub.ipynb` to find all groups and share with TAs
14 |
15 | Google sheet: https://docs.google.com/spreadsheets/d/1AZYHlwfQSungF-ptTHd1wDq5AgoxFvwVHgAwGQYY12A/edit#gid=0
16 |
17 | For GDPR: The sheet with student IDs will be send out by e-mail.
18 | 4. Use `2. PeerFeedbackAssignments.ipynb` to find allocate peerfeedback and share with students
19 |
20 | GoogleSheet: https://docs.google.com/spreadsheets/d/1Bdg7pGXWVCoRVBYZOgzccUMIqmttagraXd52B41Hwko/edit?usp=sharing
21 | 5. Use `3. RepoVisibility.ipynb` before the exam so repositories are private
22 |
23 | # Classroom
24 |
25 | Classroom: https://classroom.github.com/a/R_4UuvGk
26 |
27 | # E-mails
28 |
29 | 1. Welcome
30 |
31 | Attach 0 - Introduction/Introduction.pdf
32 | Course web page: https://sites.google.com/view/numeconcph-introprog/home
33 | Course Youtube channel: https://www.youtube.com/@numeconcph
34 | DataCamp: (not shared here)
35 | 2. Inaugural project + Git + second physical lecture
36 |
37 | Form: https://forms.gle/fFmasbkmfWzY8brw9
38 | Questions
39 | Group formation (https://sites.google.com/view/numeconcph-introprog/guides/git)
40 | Live coding
41 | 3. Peerfeedback on inaugural project (send excel document)
42 | 4. Data project
43 | 5. Midterm evaluation (https://forms.gle/r2fJ2aT924nxW6dQ7)
44 | 6. Model project
45 | 7. Third physical lecture + exam (https://forms.gle/4sw7dYm2kdjQyNn26)
46 |
47 | # YouTube
48 |
49 | Channel: https://www.youtube.com/@numeconcph
50 | Absalon: Upload copies to avoid adds.
51 |
52 | Recording: CAMTASIA 8.6 (requires license key)
53 |
54 | 1. Restart + Clear Outputs of All Cells
55 | 2. Settings:
56 |
57 | Jupyter-TOC:*Min Header Level = 2* and add *Numbering*
58 | Font size: 16
59 | Zoom: 1 on laptop, 3 on screen
60 |
61 | 3. Webcam in lower right above status bar
62 | 4. Save as project
63 | 5. Tools – Sharing -> Batch -> MP4 only (up to 1080p)
64 |
65 | Uploading:
66 |
67 | 1. Needs verified account for > 15 min
68 | 2. Description:
69 |
70 | For "Introduction to Programming and Numerical Analysis" targeting economists.
71 | See https://sites.google.com/view/numeconcph-introprog/home
72 |
73 | 3. Playlists: IntroProg-20xx
74 |
--------------------------------------------------------------------------------
/admin/groups_utils.py:
--------------------------------------------------------------------------------
1 | from github import Github # pip install PyGithub
2 |
3 |
4 | def load_repos(print_groups=False,check_year=False):
5 | # load personal access token
6 | with open(r"C:\Users\hms467\OneDrive - University of Copenhagen\Documents\Arbejde\Undervisning\IPNA_2024\Github\token.txt", mode = "r") as file:
7 | token = file.read()
8 |
9 | year = "2024"
10 | class_name = "projects-" + year
11 |
12 |
13 | # a. access github through access token.
14 | gh = Github(token)
15 | org = gh.get_organization('NumEconCopenhagen')
16 | all_repos = org.get_repos()
17 |
18 | # b. locate all repositories in current class
19 | disregard_repos = ['lucas-og-anna']
20 | add_repos = ['Projects_2024_Emma-Anna-Oscar']
21 |
22 | current_class = [repo.name for repo in all_repos if ( (class_name in repo.name) & (repo.name not in disregard_repos) ) or (repo.name in add_repos)]
23 |
24 | if print_groups:
25 | # see this years' repos
26 | for r in current_class:
27 | print(r.removeprefix(class_name+"-"))
28 |
29 |
30 | if check_year:
31 | print(f'\nFollowing repos are not in the class but includes the year {year}:')
32 |
33 | for repo in all_repos:
34 | if (year in repo.name) & (repo.name not in current_class):
35 | print(repo.name)
36 |
37 |
38 | return class_name, all_repos, current_class, disregard_repos
--------------------------------------------------------------------------------
/exams/2019/ASAD.py:
--------------------------------------------------------------------------------
1 | from numba import njit
2 |
3 | @njit
4 | def y_eq_func(ylag,pilag,v,s,slag,alpha,h,b,phi,gamma):
5 | """ equilibrium value for output
6 |
7 | Args:
8 |
9 | ylag (float): lagged output
10 | pilag (float): lagged inflation
11 | v (float): demand disturbance
12 | s (float): supply disturbance
13 | slag (float): lagged supply disturbance
14 | alpha (float): sensitivity of demand to real interest rate
15 | h (float): coefficient on inflation in Taylor rule
16 | b (float): coefficient on output in Taylor rule
17 | phi (float): degree of stickiness in inflation expectations
18 | gamma (float): effect of output on inflation in SRAS
19 |
20 | Returns:
21 |
22 | (float): equilibrium value for output
23 |
24 | """
25 |
26 | return 1/(alpha*b+alpha*gamma*h+1)*(-pilag*alpha*h+alpha*gamma*h*phi*ylag+alpha*h*phi*slag-alpha*h*s+v)
27 |
28 | @njit
29 | def pi_eq_func(ylag,pilag,v,s,slag,alpha,h,b,phi,gamma):
30 | """ equilibrium value for inflation
31 |
32 | Args:
33 |
34 | ylag (float): lagged output
35 | pilag (float): lagged inflation
36 | v (float): demand disturbance
37 | s (float): supply disturbance
38 | slag (float): lagged supply disturbance
39 | alpha (float): sensitivity of demand to real interest rate
40 | h (float): coefficient on inflation in Taylor rule
41 | b (float): coefficient on output in Taylor rule
42 | phi (float): degree of sticiness in inflation expectations
43 | gamma (float): effect of output on inflation in SRAS
44 |
45 | Returns:
46 |
47 | (float): equilibrium value for inflation
48 |
49 | """
50 |
51 | return 1/(alpha*h)*(v-1/(alpha*b+alpha*gamma*h+1)*(alpha*b+1)*(-pilag*alpha*h+alpha*gamma*h*phi*ylag+alpha*h*phi*slag-alpha*h*s+v))
52 |
53 | @njit
54 | def _simulate(y,pi,v,s,x_raw,c_raw,alpha,h,b,phi,gamma,delta,omega,sigma_x,sigma_c,T):
55 | """ equilibrium value for output
56 |
57 | Args:
58 |
59 | y (ndarray): output
60 | pi (ndarray): inflation
61 | v (ndarray): demand disturbance
62 | s (ndarray): supply disturbance
63 | x_raw (ndarray): raw demand shock
64 | c_raw (ndarray): raw supply shock
65 | alpha (float): sensitivity of demand to real interest rate
66 | h (float): coefficient on inflation in Taylor rule
67 | b (float): coefficient on output in Taylor rule
68 | phi (float): degree of sticiness in inflation expectations
69 | gamma (float): effect of output on inflation in SRAS
70 | delta (float): persistence of demand shocks
71 | omega (float): persistence of supply shocks
72 | sigma_x (float): std. of demand shocks
73 | sigma_x (float): std. of supply shocks
74 |
75 | """
76 |
77 | for t in range(1,T):
78 |
79 | # a. shocks
80 | v[t] = delta*v[t-1] + sigma_x*x_raw[t]
81 | s[t] = omega*s[t-1] + sigma_c*c_raw[t]
82 |
83 | # b. output
84 | y[t] = y_eq_func(y[t-1],pi[t-1],v[t],s[t],s[t-1],alpha,h,b,phi,gamma)
85 |
86 | # c. inflation
87 | pi[t] = pi_eq_func(y[t-1],pi[t-1],v[t],s[t],s[t-1],alpha,h,b,phi,gamma)
88 |
89 | def simulate(par,sim,T):
90 | _simulate(sim['y'],sim['pi'],sim['v'],sim['s'],sim['x_raw'],sim['c_raw'],
91 | par['alpha'],par['h'],par['b'],par['phi'],par['gamma'],
92 | par['delta'],par['omega'],par['sigma_x'],par['sigma_c'],T)
--------------------------------------------------------------------------------
/exams/2021/country_region.xlsx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NumEconCopenhagen/IntroProg-lectures/bfd33808dbfc7bf3e60851b608c50d1bb0fbb5c2/exams/2021/country_region.xlsx
--------------------------------------------------------------------------------
/exams/2021/poly_algo_fig.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NumEconCopenhagen/IntroProg-lectures/bfd33808dbfc7bf3e60851b608c50d1bb0fbb5c2/exams/2021/poly_algo_fig.png
--------------------------------------------------------------------------------
/exams/2022/question3.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 |
3 | def f_approx(x, f, N, M):
4 | '''Chebyshev approximation of the function f at x
5 |
6 | Args:
7 | x (ndarray) : the points at which to approximate f
8 | f (callable) : function to approximate
9 | N (int) : degree of approximation
10 | M (int) : number of evaluation points of f
11 |
12 | Returns:
13 | f_hat (ndarray) : approximation to f at x
14 | '''
15 |
16 | # Basis function T
17 | T = lambda i,x: np.cos(i*np.arccos(x))
18 |
19 | # Step 1: find nodes on function interval
20 | k = np.arange(1, M+1)
21 | zk = -np.cos(np.pi*(2*k-1)/(2*M))
22 |
23 | # Step 2: evaluate true function at nodes
24 | yk = f(zk)
25 |
26 | # Step 3: compute coefficients ai for approximation. ai has dimension (N+1)x1
27 | ai = np.empty((N+1,1))
28 | for i in range(N+1):
29 | ai[i] = (yk @ T(i,zk))/np.sum(T(i,zk)**2)
30 |
31 | # Create matrix of x with repeated values in row dimension for convenient multiplication
32 | # X has dimension (N+1) x J, where J is number of elements in x
33 | x = np.reshape(x,(len(x),1))
34 | X = np.transpose(np.tile(x, (1,N+1)))
35 | iN = np.arange(N+1, dtype='int')
36 | iN = iN[np.newaxis,:]
37 |
38 | # Step 4.1: compute T(x) at all i=0,..,N (row-dimension) for all elements in x (column-dimension)
39 | Tx = T(iN.T, X)
40 |
41 | # Step 4.2: compute approximation by summing the product of coefficients ai and node values Ti(x)
42 | # Note that ai is multiplied with each column of Tx, which represent a separate point to approximate f at.
43 | f_hat = np.sum(ai*Tx, axis=0)
44 |
45 | return f_hat
46 |
--------------------------------------------------------------------------------
/exams/2022/re_solution_2022/question1.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 |
3 | def log_likelihood(theta, x, y, mp):
4 | ''' Computes the sum of log-likelihood contributions for observations given beta
5 |
6 | Args:
7 |
8 | beta (ndarray): coefficients to independent variables
9 | x (ndarray): independent variables
10 | y (ndarray): observed binary choices
11 |
12 | Returns:
13 |
14 | (float): sum of log-likelihood contributions
15 |
16 | '''
17 | b1 = theta[0]
18 | b2 = theta[1] + theta[2] * mp.Z # Drawing individual slopes
19 | b2 = np.broadcast_to(b2, (mp.N, mp.Ndraws))
20 |
21 | xb = b1 + x*b2
22 | z = np.exp(xb)
23 | y_prb = z / (1 + z)
24 |
25 | # Calculate likelihood contributions
26 | y_dens = y*y_prb + (1-y)*(1-y_prb)
27 | y_mc = np.mean(y_dens, axis=1)
28 | ll = np.sum(np.log(y_mc))
29 | return ll
30 |
--------------------------------------------------------------------------------
/exams/2022/re_solution_2022/question2.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 |
3 | def utility(w, s):
4 | ''' Utility from social media platform usage.
5 | '''
6 | return w + np.sum(s, axis=0)
7 |
8 |
9 | def utility_young(w, s):
10 | ''' Utility from social media platform usage for young users.
11 | '''
12 | return w + s[0] - s[1]
13 |
14 |
15 | def ccp(u):
16 | ''' Choice probabilities.
17 | '''
18 | return np.exp(u) / np.sum(np.exp(u), axis=1, keepdims=True)
19 |
20 |
21 | def share(prb, mp):
22 | return prb[0]*mp.rho_y + prb[1]*mp.rho_o
23 |
24 |
25 |
26 | def some_eq(mp, s0):
27 | ''' Find user distribution on social media platforms by succesive approximations
28 |
29 | Args:
30 | mp (SimpleNamespace): model parameters
31 | s0 (ndarray): initial vector of user shares for platforms
32 |
33 | Returns:
34 | (ndarray): equilibrium share of each user type on each platform
35 | '''
36 |
37 | # a.1 Collect population shares in vector
38 | rho = np.array([[mp.rho_y],[mp.rho_o]])
39 |
40 | # a.2 Use desired specification of utility for the young and the old
41 | u_old = lambda s : utility(mp.w_o, s)
42 | if (mp.utility_spec == 'baseline'):
43 | u_young = lambda s: utility(mp.w_y,s)
44 | elif (mp.utility_spec == 'negative_externality'):
45 | u_young = lambda s: utility_young(mp.w_y,s)
46 | else:
47 | raise Exception('wrong utility specification')
48 |
49 | # a.3 Compute initial utility vector
50 | s = np.stack((s0,s0)) * rho
51 | uk_young = u_young(s)
52 | uk_old = u_old(s)
53 | u_prev = np.stack((uk_young,uk_old))
54 | k = 0
55 |
56 | # b.1 Succesive approximation loop
57 | while k < mp.N:
58 | s = ccp(u_prev) * rho
59 | uk_young = u_young(s)
60 | uk_old = u_old(s)
61 | u_nxt = np.stack((uk_young, uk_old))
62 |
63 | if np.amax(np.abs(u_prev - u_nxt)) < mp.eps:
64 | break
65 | else:
66 | k = k+1
67 | u_prev = u_nxt
68 |
69 | s_total = np.sum(s, axis=0)
70 | s_age = s / rho
71 | return s_total, s_age, u_nxt
72 |
--------------------------------------------------------------------------------
/exams/2022/re_solution_2022/question3.py:
--------------------------------------------------------------------------------
1 | from math import isnan
2 | from math import fabs
3 | import numpy as np
4 |
5 |
6 | def safe_NR(f, fprime, a, b, x0=np.nan, maxit=20, tol=1e-14, do_print=False):
7 | """
8 | Root of f(x) obtained by switching between Newton-Raphson and bisection.
9 | Args:
10 | f (function): function to find root of
11 | fprime (function): derivative of f
12 | a (float): lower bound
13 | b (float): upper bound
14 | x0 (float): initial guess of root
15 | maxit (int): maximum number of iterations
16 | tol (float): tolerance
17 | """
18 | """
19 | Numerical root of f(x) = 0 by Newton-Raphson method
20 | """
21 |
22 | if a > b :
23 | raise Exception('Lower bound exceeds upper bound')
24 |
25 | if f(a)*f(b) > 0:
26 | raise Exception(
27 | 'There is either no root in interval or multiple roots. sign(f(a)) == sign(f(b))')
28 |
29 | if (fabs(f(a)) < tol):
30 | return a
31 |
32 | if (fabs(f(b)) < tol):
33 | return b
34 |
35 | # Initial values
36 | if isnan(x0):
37 | x = (b+a)/2
38 | else:
39 | x = x0
40 |
41 | fx = f(x)
42 | dfx = fprime(x)
43 | j = 0
44 |
45 | NRstep = lambda x,fx,dfx : x - fx/dfx
46 | Bstep = lambda a,b: a + 0.5*(b-a)
47 |
48 | while j < maxit:
49 | cond1 = not (a < NRstep(x,fx,dfx) < b)
50 | if cond1:
51 | # Go Bisection if Newton-Raphson goes off bounds or goes too slow
52 | xn = Bstep(a,b)
53 | steptype = 'bisection'
54 | else:
55 | # Default to Newton-Raphson
56 | xn = NRstep(x,fx,dfx)
57 | steptype = 'newton'
58 |
59 | if do_print:
60 | print(j, ' a:', a, 'b:', b, 'xn:', xn, 'fxn:', f(xn), ' steptype:', steptype)
61 |
62 | x = xn
63 | fx = f(x)
64 | dfx = fprime(x)
65 |
66 | if fabs(fx) < tol:
67 | break
68 |
69 | # Update bounds. Needed for both bisection and Newton-Rapshon
70 | if fx*f(a) < 0.0:
71 | b = x
72 | else:
73 | a = x
74 |
75 | j = j+1
76 |
77 | return x,j
78 |
--------------------------------------------------------------------------------
/exams/2022/windmills.xlsx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NumEconCopenhagen/IntroProg-lectures/bfd33808dbfc7bf3e60851b608c50d1bb0fbb5c2/exams/2022/windmills.xlsx
--------------------------------------------------------------------------------
/exams/2023/re_solution_2023/Problem_1.py:
--------------------------------------------------------------------------------
1 | from types import SimpleNamespace
2 | import numpy as np
3 | from scipy import optimize
4 | import matplotlib.pyplot as plt
5 |
6 | class Consumer():
7 |
8 | def __init__(self,par):
9 | """ initialize """
10 |
11 | self.par = par
12 | self.sol = SimpleNamespace()
13 | self.sim = SimpleNamespace()
14 |
15 | self.allocate()
16 |
17 | def allocate(self):
18 | """ allocate memory """
19 |
20 | sim = self.sim
21 | par = self.par
22 |
23 | sim.psi2 = np.empty(par.N)
24 | sim.nu2 = np.empty(par.N)
25 | sim.y2 = np.empty(par.N)
26 |
27 | def utility(self,c):
28 | """ utility function"""
29 |
30 | par = self.par
31 | return c**(1-par.rho) / (1-self.par.rho)
32 |
33 | def utility_period1(self,c1):
34 | """ utility in period 1 """
35 |
36 | par = self.par
37 | sim = self.sim
38 |
39 | a1 = par.m1-c1
40 | m2 = (1+par.r)*a1 + sim.y2
41 |
42 | return (self.utility(c1) + par.beta*np.mean(self.utility(m2)) )
43 |
44 |
45 | def simulate(self,seed= None):
46 | """ simulate the model """
47 |
48 | par = self.par
49 | sim = self.sim
50 |
51 | np.random.seed(seed)
52 |
53 | sim.psi2[:] = np.random.normal(0,1, par.N)
54 | sim.nu2[:] = np.random.uniform(low=0,high=1,size=par.N)
55 |
56 | self.calc_y2()
57 |
58 | def calc_y2(self):
59 | """ calculate y2 """
60 |
61 | par = self.par
62 | sim = self.sim
63 |
64 | sim.y2[:] = np.exp(-0.5*par.sigma_psi**2+par.sigma_psi*sim.psi2)
65 | I = sim.nu2 excess goods demand -> {error1:14.8f} {error2:14.8f}')
181 | break
182 |
183 | # Print
184 | if do_print:
185 | if t < 5 or t%25 == 0:
186 | print(f'{t:3d}: p1 = {p1:6.8f}, p2 = {p2:6.8f} -> excess goods demand -> {error2:14.8f} {error2:14.8f}')
187 | elif t == 5:
188 | print(' ...')
189 |
190 |
191 | # Update prices
192 |
193 | p1 -= par.step*error1
194 | p2 -= par.step*error2
195 |
196 |
197 | # No more iterations?
198 | if t >= par.maxiter:
199 | print('Solution not found!')
200 | break
201 |
202 | t += 1
203 |
204 | return p1,p2
205 |
206 |
207 | def optimal_gov(self):
208 | '''
209 | Find optimal values of tau and T for the government
210 | '''
211 | par = self.par
212 | sol = self.sol
213 |
214 | def value_of_choice_gov(tau,do_print=False):
215 | par = self.par
216 | sol = self.sol
217 |
218 | par.tau = tau
219 |
220 | self.solve(guess='sol')
221 |
222 | self.firms(sol.p1,sol.p2)
223 | par.T = par.tau*sol.y2
224 | self.households(sol.p1,sol.p2)
225 |
226 | SWF = sol.U - par.kappa*sol.y2
227 |
228 | if do_print: print(f'{tau = :.3f}, {sol.U = :.4f}, {sol.y2 = :.4f}, {SWF = :.5f}')
229 |
230 | return -SWF
231 |
232 |
233 | taus = np.linspace(0.0,0.3,20)
234 | SWFs = np.zeros_like(taus)
235 |
236 | print('Grid search:')
237 | for i,tau in enumerate(taus):
238 | SWFs[i] = -value_of_choice_gov(tau,do_print=True)
239 |
240 | i = np.argmax(SWFs)
241 | tau_min = taus[i-1]
242 | tau_max = taus[i+1]
243 |
244 | res = optimize.minimize_scalar(value_of_choice_gov,bounds=(tau_min,tau_max),method='bounded')
245 |
246 | print('\nOptimal tau:')
247 | value_of_choice_gov(res.x,do_print=True)
248 | T = par.tau*sol.y2
249 | print(f'Gvining optimal T = {T:.2f}')
250 |
251 |
252 | fig = plt.figure()
253 | ax = fig.add_subplot(1,1,1)
254 | ax.plot(taus,SWFs,label='SWF');
255 | ax.axvline(res.x,ls='--',color='k',label=r'Optimal $\tau$')
256 |
257 | ax.set_xlabel(r'$\tau$')
258 | ax.legend()
259 | fig.tight_layout()
--------------------------------------------------------------------------------
/exams/2024/solution_2024/Problem_2.py:
--------------------------------------------------------------------------------
1 | from types import SimpleNamespace
2 | import numpy as np
3 | import matplotlib.pyplot as plt
4 | from matplotlib.ticker import MaxNLocator
5 | plt.rcParams.update({"axes.grid":True,"grid.color":"black","grid.alpha":"0.25","grid.linestyle":"--"})
6 |
7 |
8 | class CareerChoiceModel:
9 |
10 | def __init__(self,**kwargs):
11 | '''
12 | Initialize model with parameters
13 | '''
14 |
15 | self.par = par = SimpleNamespace()
16 | self.sim = sim = SimpleNamespace()
17 |
18 |
19 |
20 | for key,value in kwargs.items():
21 | setattr(par,key,value)
22 |
23 | self.allocate()
24 |
25 |
26 | def allocate(self):
27 | '''
28 | Allocate size of arrays for simulation
29 | '''
30 |
31 | par = self.par
32 | sim = self.sim
33 |
34 | sim.eps = np.empty((par.N,par.J,par.K))
35 | sim.choice = np.empty((par.N,par.K),dtype=int)
36 | sim.choice2 = np.empty((par.N,par.K),dtype=int)
37 |
38 |
39 | sim.uf = np.empty((par.N,par.J,par.K))
40 | sim.u_ante = np.empty((par.N,par.K))
41 | sim.u_chosen = np.empty((par.N,par.K))
42 | sim.u_ante2 = np.empty((par.N,par.K))
43 | sim.u_chosen2 = np.empty((par.N,par.K))
44 |
45 | sim.u_post = np.empty((par.N,par.J,par.K))
46 |
47 | sim.uf2 = np.empty((par.N,par.J,par.K))
48 |
49 |
50 | def simulate(self):
51 | '''
52 | Simulate shocks and calculate utility of all choices
53 | '''
54 |
55 | # Draw epsilon:
56 | par = self.par
57 | sim = self.sim
58 |
59 | sim.eps[:] = np.random.normal(0,scale=par.sigma, size=sim.eps.shape)
60 |
61 | # Calculate utility
62 | sim.u_post[:,:,:] = par.v.reshape((1,par.J,1)) + sim.eps
63 |
64 | # draw friends
65 | for i in range(par.N):
66 | sim.uf[i,:,:] = par.v.reshape((1,par.J,1)) + np.random.normal(0,scale=par.sigma, size=(par.J,par.F[i],par.K)).mean(axis=1)
67 |
68 |
69 |
70 | def utility(self):
71 | '''
72 | Determine career choice and store chosen utility
73 | '''
74 | par = self.par
75 | sim = self.sim
76 |
77 |
78 | sim.choice[:] = np.argmax(sim.uf, axis=1)
79 |
80 |
81 | sim.u_ante[:,:] = np.take_along_axis(sim.uf, np.expand_dims(sim.choice, axis=1), axis=1).reshape((par.N,par.K)) # Expected utility from choice
82 | sim.u_chosen[:] = np.take_along_axis(sim.u_post, np.expand_dims(sim.choice, axis=1), axis=1).reshape((par.N,par.K)) # Realized utility from choice
83 |
84 |
85 | def career_change(self):
86 | '''
87 | Calculate utility of changing jobs and whether it is optimal
88 | '''
89 |
90 | sim = self.sim
91 | par = self.par
92 |
93 | # Expected utility for each job with cost of changing
94 | sim.uf2[:] = sim.uf - par.c
95 |
96 | # Replace with known utility for the jobs they have chosen
97 | for j in range(par.J):
98 | sim.uf2[:,j,:][sim.choice==j] = sim.u_chosen[sim.choice==j]
99 |
100 |
101 | sim.choice2[:] = np.argmax(sim.uf2, axis=1)
102 |
103 |
104 | sim.u_ante2[:,:] = np.take_along_axis(sim.uf2, np.expand_dims(sim.choice2, axis=1), axis=1).reshape((par.N,par.K)) # Expected utility from choice
105 | sim.u_chosen2[:] = np.take_along_axis(sim.u_post, np.expand_dims(sim.choice2, axis=1), axis=1).reshape((par.N,par.K)) - par.c*(sim.choice2 != sim.choice) # Realized utility from choice
106 |
107 |
108 |
109 | def plot_post_utility(self):
110 | par = self.par
111 | sim = self.sim
112 |
113 | fig, ax = plt.subplots()
114 |
115 |
116 | mean_u_post = sim.u_post.mean(axis=(0,2))
117 |
118 |
119 | ax.scatter(range(par.J), mean_u_post,label='Average realized utility')
120 | ax.scatter(range(par.J), par.v, label='Expected utility',s=8)
121 |
122 |
123 | ax.set_title('Utility of jobs')
124 | ax.set_xlabel('Job')
125 | ax.set_ylabel('Utility')
126 | ax.xaxis.set_major_locator(MaxNLocator(integer=True))
127 | ax.legend()
128 |
129 |
130 | def plot_utility(self):
131 | par = self.par
132 | sim = self.sim
133 |
134 |
135 | # Mean utility vs number of friends
136 | mean_u_ante = sim.u_ante.mean(axis=1)
137 | mean_u_chosen = sim.u_chosen.mean(axis=1)
138 |
139 |
140 | fig, ax = plt.subplots()
141 |
142 |
143 | x_vec = np.arange(1,par.N+1)
144 | ax.scatter(x_vec, mean_u_ante, label='Expected utility',color='purple')
145 | ax.scatter(x_vec, mean_u_chosen, label='Realized utility',color='red')
146 |
147 |
148 | ax.xaxis.set_major_locator(MaxNLocator(integer=True))
149 | ax.set_title('Mean Utility vs number of friends')
150 | ax.set_xlabel('Number of Friends')
151 | ax.set_ylabel('Mean Utility')
152 |
153 | ax.legend()
154 |
155 |
156 | # Share choosing each job vs number of friends
157 | fig, ax = plt.subplots()
158 |
159 |
160 |
161 | bottom = np.zeros(par.N)
162 | for j in range(par.J):
163 | share = (sim.choice==j).mean(axis=1)*100
164 | ax.bar(x_vec, share, bottom=bottom, label=f'Share choosing job {j+1}')
165 | bottom += share
166 |
167 |
168 | ax.set_title('Share choosing each job vs number of friends')
169 | ax.set_xlabel('Number of Friends')
170 | ax.set_ylabel('Share, %')
171 |
172 | ax.legend()
173 |
174 |
175 | def plot_utility_change(self):
176 | sim = self.sim
177 | par = self.par
178 |
179 |
180 | # Mean utility vs number of friends
181 | mean_u_ante = sim.u_ante2.mean(axis=1)
182 | mean_u_chosen2 = sim.u_chosen2.mean(axis=1)
183 |
184 | mean_u_chosen = sim.u_chosen.mean(axis=1)
185 |
186 | fig, ax = plt.subplots()
187 |
188 | x_vec = np.arange(1,par.N+1)
189 | ax.scatter(x_vec, mean_u_ante, label='Expected utility',color='purple')
190 | ax.scatter(x_vec, mean_u_chosen2, label='Realized utility',color='red')
191 | ax.scatter(x_vec, mean_u_chosen, label='Chosen utility in earlier period',color='pink')
192 |
193 |
194 | ax.xaxis.set_major_locator(MaxNLocator(integer=True))
195 | ax.set_title('Mean Utility vs number of friends')
196 | ax.set_xlabel('Number of Friends')
197 | ax.set_ylabel('Mean Utility')
198 |
199 | ax.legend()
200 |
201 |
202 | # Share that change jobs conditional on job chosen in the first period
203 |
204 | fig, ax = plt.subplots()
205 |
206 | for j in range(par.J):
207 | share = np.average(sim.choice2 != sim.choice,axis=1, weights= (sim.choice==j)) *100
208 | ax.scatter(x_vec, share, label=f'Share initially in job {j+1} that change jobs')
209 | ax.set_title('Share that change jobs vs number of friends')
210 | ax.set_xlabel('Number of Friends')
211 | ax.set_ylabel('Share, %')
212 | ax.legend()
213 |
214 |
215 |
216 | fig, ax = plt.subplots()
217 |
218 |
219 | share = np.average(sim.choice2 != sim.choice,axis=1) *100
220 | ax.scatter(x_vec, share)
221 | ax.set_title('Share that change jobs vs number of friends')
222 | ax.set_xlabel('Number of Friends')
223 | ax.set_ylabel('Share, %')
224 |
--------------------------------------------------------------------------------
/projects/2020/InauguralProject2020.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NumEconCopenhagen/IntroProg-lectures/bfd33808dbfc7bf3e60851b608c50d1bb0fbb5c2/projects/2020/InauguralProject2020.pdf
--------------------------------------------------------------------------------
/projects/2021/InauguralProject2021.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NumEconCopenhagen/IntroProg-lectures/bfd33808dbfc7bf3e60851b608c50d1bb0fbb5c2/projects/2021/InauguralProject2021.pdf
--------------------------------------------------------------------------------
/projects/2021/InauguralProject2021.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | from scipy import optimize
3 |
4 | def u_func(c, h, mp):
5 | """ Calculates utility of chosen (consumption, housing) bundle.
6 |
7 | Args:
8 |
9 | c (float): consumption
10 | h (float): housing
11 | mp (dict): model parameters.
12 |
13 | Returns:
14 |
15 | (float): utility of bundle
16 | """
17 | return (c**(1-mp['phi']))*(h**mp['phi'])
18 |
19 | def tau(h, mp, p = 1):
20 | """ Calculates total housing taxes when choosing h
21 |
22 | Args:
23 |
24 | h (float): housing
25 | mp (dict): model parameters.
26 | p (float): price index of housing
27 |
28 | Returns:
29 |
30 | (float): total taxes paid for a house of quality h
31 |
32 | """
33 |
34 | # Calculate assessment of home. Equation (2).
35 | p_tilde = p*h*mp['epsilon']
36 | return mp['tau_g']*p_tilde + mp['tau_p']*(max(p_tilde - mp['p_bar'], 0))
37 |
38 | def user_cost(h, mp, p=1):
39 | """ Get total usercosts of housing, taxes and mortgage payments. Equation (4)
40 |
41 | Args:
42 |
43 | h (float): housing
44 | mp (dict): model parameters.
45 | p (float): price index of housing
46 |
47 | Returns:
48 |
49 | (float): total user costs of housing.
50 |
51 | """
52 | taxes = tau(h, mp, p)
53 | interest = mp['r']*h*p
54 |
55 | return interest + taxes
56 |
57 | def choose_c(h, m, mp, p=1):
58 | """ Implicit choice of consumption given housing choice. Derived from Equation (3).
59 |
60 | Args:
61 | h (float): housing
62 | m (float): cash-on-hand
63 | mp (dict): model parameters.
64 | p (float): price index of housing
65 |
66 | Returns:
67 |
68 | (float) : consumption given choice of housing and budget constraint.
69 |
70 | """
71 |
72 | return m - user_cost(h, mp, p)
73 |
74 | def value_of_choice(h, m, mp, p=1):
75 | """ Criterion function for optimizer.
76 |
77 | Args:
78 |
79 | h (float): housing
80 | m (float): cash-on-hand
81 | mp (dict): model parameters.
82 | p (float): price index of housing
83 |
84 | Returns:
85 |
86 | (float): negative of utility function at (c,h) consumption bundle and cash-on-hand.
87 |
88 | """
89 |
90 | c = choose_c(h, m, mp, p)
91 | return -u_func(c, h, mp)
92 |
93 | def solve_housing(m, mp, print_sol=True, p=1):
94 | """ Solve the consumers problem given cash-on-hand and model parameters
95 |
96 | Args:
97 | mp (dict): model parameters.
98 | m (float): cash-on-hand
99 | print_sol (bool): print solution to console
100 | p (float): price index of housing
101 |
102 | Returns:
103 |
104 | c (float): optimal consumption
105 | h (float): optimal housing
106 | u (float): utility at solution
107 |
108 | """
109 |
110 | # Call optimizer
111 | sol = optimize.minimize_scalar(value_of_choice, bounds=None,
112 | args=(m, mp, p))
113 |
114 | if print_sol:
115 | print_solution(sol, m, mp, p)
116 |
117 | # Unpack solution
118 | h = sol.x
119 | c = choose_c(h, m, mp, p)
120 | u = u_func(c, h, mp)
121 | return c, h, u
122 |
123 |
124 | def tax_revenues(mp, ms, p=1):
125 | """ Calculates the tax revenue associated with each consumer in the population and its optimal housing
126 |
127 | Args:
128 | mp (dict): model parameters.
129 | ms (np.array): cash-on-hand
130 | p (float): price index of housing
131 |
132 | Returns:
133 |
134 | (float): distribution of collected housing tax revenue
135 |
136 | """
137 |
138 | h_star = np.empty((len(ms),))
139 | tax_revenue = np.empty((len(ms),))
140 |
141 | for i,m in enumerate(ms):
142 | c, h, u = solve_housing(m, mp, print_sol=False, p=p)
143 | h_star[i] = h
144 | tax_revenue[i] = tau(h, mp)
145 |
146 | return tax_revenue, h_star
147 |
148 | def print_solution(sol, m, mp, p=1):
149 | """ Print solution of consumer problem
150 |
151 | Args:
152 | sol (OptimizeResult): solution object from scipy.optimize
153 | m (float): cash-on-hand
154 | mp (dict): model parameters.
155 |
156 | Returns:
157 |
158 | """
159 |
160 | h = sol.x
161 | c = choose_c(h, m, mp, p)
162 | u = u_func(c, h, mp)
163 |
164 | # Print
165 | print(f'c = {c:6.3f}')
166 | print(f'h = {h:6.3f}')
167 | print(f'user_costs = {user_cost(h, mp, p):6.3f}')
168 | print(f'u = {u:6.3f}')
169 | print(f'm - user_costs - c = {m - user_cost(h, mp, p) - c:.4f}')
170 |
--------------------------------------------------------------------------------
/projects/2022/InauguralProject2022.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NumEconCopenhagen/IntroProg-lectures/bfd33808dbfc7bf3e60851b608c50d1bb0fbb5c2/projects/2022/InauguralProject2022.pdf
--------------------------------------------------------------------------------
/projects/2022/InauguralProject2022.py:
--------------------------------------------------------------------------------
1 | from types import SimpleNamespace
2 | import math
3 | import numpy as np
4 | from scipy import optimize
5 |
6 | def util(y,theta=-2):
7 | return (y**(1+theta))/(1+theta)
8 |
9 | def premium(q,p,l=0):
10 | return (1+l)*p*q
11 |
12 | def V(q,x,y,p):
13 | z = y-x+q-premium(q,p)
14 | return p*util(z) + (1-p)*util(y-premium(q,p))
15 |
16 | def V_tilde(pi,p,q,x,y):
17 | z = y-x+q-pi
18 | return p*util(z) + (1-p)*util(y-pi)
19 |
20 |
21 | def opt_q(x, y, p):
22 | def crit(q): return -V(q, x, y, p)
23 |
24 | q0 = [x/2]
25 |
26 | sol = optimize.minimize_scalar(
27 | crit, q0, method='bounded', bounds=(1e-5, x))
28 |
29 | return sol
30 |
31 | def mc_value(mp, pol):
32 | x = np.random.beta(mp.a, mp.b, mp.N)
33 | z = mp.y - (1-pol.gamma)*x - pol.pi
34 | val = np.mean(util(z))
35 | return val
36 |
37 |
--------------------------------------------------------------------------------
/projects/2023/HouseholdSpecializationModel.py:
--------------------------------------------------------------------------------
1 |
2 | from types import SimpleNamespace
3 |
4 | import numpy as np
5 | from scipy import optimize
6 |
7 | import pandas as pd
8 | import matplotlib.pyplot as plt
9 |
10 | class HouseholdSpecializationModelClass:
11 |
12 | def __init__(self):
13 | """ setup model """
14 |
15 | # a. create namespaces
16 | par = self.par = SimpleNamespace()
17 | sol = self.sol = SimpleNamespace()
18 |
19 | # b. preferences
20 | par.rho = 2.0
21 | par.nu = 0.001
22 | par.epsilon = 1.0
23 | par.omega = 0.5
24 |
25 | # c. household production
26 | par.alpha = 0.5
27 | par.sigma = 1.0
28 |
29 | # d. wages
30 | par.wM = 1.0
31 | par.wF = 1.0
32 | par.wF_vec = np.linspace(0.8,1.2,5)
33 |
34 | # e. targets
35 | par.beta0_target = 0.4
36 | par.beta1_target = -0.1
37 |
38 | # f. solution
39 | sol.LM_vec = np.zeros(par.wF_vec.size)
40 | sol.HM_vec = np.zeros(par.wF_vec.size)
41 | sol.LF_vec = np.zeros(par.wF_vec.size)
42 | sol.HF_vec = np.zeros(par.wF_vec.size)
43 |
44 | sol.beta0 = np.nan
45 | sol.beta1 = np.nan
46 |
47 | def calc_utility(self,LM,HM,LF,HF):
48 | """ calculate utility """
49 |
50 | par = self.par
51 | sol = self.sol
52 |
53 | # a. consumption of market goods
54 | C = par.wM*LM + par.wF*LF
55 |
56 | # b. home production
57 | H = HM**(1-par.alpha)*HF**par.alpha
58 |
59 | # c. total consumption utility
60 | Q = C**par.omega*H**(1-par.omega)
61 | utility = np.fmax(Q,1e-8)**(1-par.rho)/(1-par.rho)
62 |
63 | # d. disutlity of work
64 | epsilon_ = 1+1/par.epsilon
65 | TM = LM+HM
66 | TF = LF+HF
67 | disutility = par.nu*(TM**epsilon_/epsilon_+TF**epsilon_/epsilon_)
68 |
69 | return utility - disutility
70 |
71 | def solve_discrete(self,do_print=False):
72 | """ solve model discretely """
73 |
74 | par = self.par
75 | sol = self.sol
76 | opt = SimpleNamespace()
77 |
78 | # a. all possible choices
79 | x = np.linspace(0,24,49)
80 | LM,HM,LF,HF = np.meshgrid(x,x,x,x) # all combinations
81 |
82 | LM = LM.ravel() # vector
83 | HM = HM.ravel()
84 | LF = LF.ravel()
85 | HF = HF.ravel()
86 |
87 | # b. calculate utility
88 | u = self.calc_utility(LM,HM,LF,HF)
89 |
90 | # c. set to minus infinity if constraint is broken
91 | I = (LM+HM > 24) | (LF+HF > 24) # | is "or"
92 | u[I] = -np.inf
93 |
94 | # d. find maximizing argument
95 | j = np.argmax(u)
96 |
97 | opt.LM = LM[j]
98 | opt.HM = HM[j]
99 | opt.LF = LF[j]
100 | opt.HF = HF[j]
101 |
102 | # e. print
103 | if do_print:
104 | for k,v in opt.__dict__.items():
105 | print(f'{k} = {v:6.4f}')
106 |
107 | return opt
108 |
109 | def solve(self,do_print=False):
110 | """ solve model continously """
111 |
112 | pass
113 |
114 | def solve_wF_vec(self,discrete=False):
115 | """ solve model for vector of female wages """
116 |
117 | pass
118 |
119 | def run_regression(self):
120 | """ run regression """
121 |
122 | par = self.par
123 | sol = self.sol
124 |
125 | x = np.log(par.wF_vec)
126 | y = np.log(sol.HF_vec/sol.HM_vec)
127 | A = np.vstack([np.ones(x.size),x]).T
128 | sol.beta0,sol.beta1 = np.linalg.lstsq(A,y,rcond=None)[0]
129 |
130 | def estimate(self,alpha=None,sigma=None):
131 | """ estimate alpha and sigma """
132 |
133 | pass
--------------------------------------------------------------------------------
/projects/2023/HouseholdSpecializationModel_sol.py:
--------------------------------------------------------------------------------
1 |
2 | from types import SimpleNamespace
3 |
4 | import numpy as np
5 | from scipy import optimize
6 |
7 | import pandas as pd
8 | import matplotlib.pyplot as plt
9 |
10 | class HouseholdSpecializationModelClass:
11 |
12 | def __init__(self):
13 | """ setup model """
14 |
15 | # a. create namespaces
16 | par = self.par = SimpleNamespace()
17 | sol = self.sol = SimpleNamespace()
18 |
19 | # b. preferences
20 | par.rho = 2.0
21 | par.nu = 0.001
22 | par.epsilon = 1.0
23 | par.omega = 0.5
24 |
25 | # c. household production
26 | par.alpha = 0.5
27 | par.sigma = 1.0
28 |
29 | # d. wages
30 | par.wM = 1.0
31 | par.wF = 1.0
32 | par.wF_vec = np.linspace(0.8,1.2,5)
33 |
34 | # e. targets
35 | par.beta0_target = 0.4
36 | par.beta1_target = -0.1
37 |
38 | # f. solution
39 | sol.LM_vec = np.zeros(par.wF_vec.size)
40 | sol.HM_vec = np.zeros(par.wF_vec.size)
41 | sol.LF_vec = np.zeros(par.wF_vec.size)
42 | sol.HF_vec = np.zeros(par.wF_vec.size)
43 |
44 | sol.beta0 = np.nan
45 | sol.beta1 = np.nan
46 |
47 | def calc_utility(self,LM,HM,LF,HF):
48 | """ calculate utility """
49 |
50 | par = self.par
51 | sol = self.sol
52 |
53 | # a. consumption of market goods
54 | C = par.wM*LM + par.wF*LF
55 |
56 | # b. home production
57 |
58 | # DELETE
59 | if np.isclose(par.sigma,0.0):
60 | H = np.fmin(HM,HF)
61 | elif np.isclose(par.sigma,1.0):
62 | H = HM**(1-par.alpha)*HF**par.alpha
63 | else:
64 | _M_term = (1-par.alpha)*np.fmax(HM,1e-8)**((par.sigma-1)/par.sigma)
65 | _F_term = par.alpha*np.fmax(HF,1e-8)**((par.sigma-1)/par.sigma)
66 | _inner = _M_term + _F_term
67 | H = np.fmax(_inner,1e-8)**(par.sigma/(par.sigma-1))
68 | # REPLACE
69 | # H = HM**(1-par.alpha)*HF**par.alpha
70 | # END
71 |
72 | # c. total consumption utility
73 | Q = C**par.omega*H**(1-par.omega)
74 | utility = np.fmax(Q,1e-8)**(1-par.rho)/(1-par.rho)
75 |
76 | # d. disutlity of work
77 | epsilon_ = 1+1/par.epsilon
78 | TM = LM+HM
79 | TF = LF+HF
80 | disutility = par.nu*(TM**epsilon_/epsilon_+TF**epsilon_/epsilon_)
81 |
82 | return utility - disutility
83 |
84 | def solve_discrete(self,do_print=False):
85 | """ solve model discretely """
86 |
87 | par = self.par
88 | sol = self.sol
89 | opt = SimpleNamespace()
90 |
91 | # a. all possible choices
92 | x = np.linspace(0,24,49)
93 | LM,HM,LF,HF = np.meshgrid(x,x,x,x) # all combinations
94 |
95 | LM = LM.ravel() # vector
96 | HM = HM.ravel()
97 | LF = LF.ravel()
98 | HF = HF.ravel()
99 |
100 | # b. calculate utility
101 | u = self.calc_utility(LM,HM,LF,HF)
102 |
103 | # c. set to minus infinity if constraint is broken
104 | I = (LM+HM > 24) | (LF+HF > 24) # | is "or"
105 | u[I] = -np.inf
106 |
107 | # d. find maximizing argument
108 | j = np.argmax(u)
109 |
110 | opt.LM = LM[j]
111 | opt.HM = HM[j]
112 | opt.LF = LF[j]
113 | opt.HF = HF[j]
114 |
115 | # e. print
116 | if do_print:
117 | for k,v in opt.__dict__.items():
118 | print(f'{k} = {v:6.4f}')
119 |
120 | return opt
121 |
122 | def solve(self,do_print=False):
123 | """ solve model continously """
124 |
125 | # DELETE
126 |
127 | sol = self.sol
128 | opt = SimpleNamespace()
129 |
130 | # a. initial guess from discrete solution
131 | opt = self.solve_discrete()
132 | x0 = np.array([opt.LM,opt.HM,opt.LF,opt.HF])
133 |
134 | # b. run optimizer
135 | def obj(x):
136 |
137 | LM = x[0]
138 | HM = x[1]
139 | LF = x[2]
140 | HF = x[3]
141 |
142 | TM = LM+HM
143 | TF = LF+HF
144 |
145 | if TM > 24:
146 | LM *= 24/TM
147 | HM *= 24/TM
148 |
149 | if TF > 24:
150 | LF *= 24/TF
151 | HF *= 24/TF
152 |
153 | return -self.calc_utility(LM,HM,LF,HF)
154 |
155 | result = optimize.minimize(obj,x0,method='Nelder-Mead',bounds=((0,24),(0,24),(0,24),(0,24)))
156 |
157 | opt.LM = result.x[0]
158 | opt.HM = result.x[1]
159 | opt.LF = result.x[2]
160 | opt.HF = result.x[3]
161 |
162 | # c. print
163 | if do_print:
164 | for k,v in opt.__dict__.items():
165 | print(f'{k} = {v:6.4f}')
166 |
167 | return opt
168 |
169 | def solve_wF_vec(self,discrete=False):
170 | """ solve model for vector of female wages """
171 |
172 | # DELTE
173 |
174 | par = self.par
175 | sol = self.sol
176 |
177 | for i,wF in enumerate(par.wF_vec):
178 |
179 | self.par.wF = wF
180 |
181 | if discrete:
182 | opt = self.solve_discrete()
183 | else:
184 | opt = self.solve()
185 |
186 | sol.LM_vec[i] = opt.LM
187 | sol.HM_vec[i] = opt.HM
188 | sol.LF_vec[i] = opt.LF
189 | sol.HF_vec[i] = opt.HF
190 |
191 | def run_regression(self):
192 | """ run regression """
193 |
194 | par = self.par
195 | sol = self.sol
196 |
197 | x = np.log(par.wF_vec)
198 | y = np.log(sol.HF_vec/sol.HM_vec)
199 | A = np.vstack([np.ones(x.size),x]).T
200 | sol.beta0,sol.beta1 = np.linalg.lstsq(A,y,rcond=None)[0]
201 |
202 | def estimate(self,alpha=None,sigma=None):
203 | """ estimate alpha and sigma """
204 |
205 | # DELTE
206 |
207 | par = self.par
208 | sol = self.sol
209 |
210 | # a. objective
211 | def obj(x):
212 |
213 | par.alpha = x[0]
214 | par.sigma = x[1]
215 |
216 | # a. solve
217 | self.solve_wF_vec()
218 |
219 | # b. run regression
220 | self.run_regression()
221 |
222 | # c. error
223 | error = (sol.beta0-par.beta0_target)**2 + (sol.beta1-par.beta1_target)**2
224 |
225 | print(f'{par.alpha = :12.8f}, {par.sigma = :12.8f}: {error = :12.8f}')
226 |
227 | return error
228 |
229 | # b. initial guess
230 | if alpha is None: alpha = par.alpha
231 | if sigma is None: sigma = par.sigma
232 |
233 | x0 = np.array([alpha,sigma])
234 | results = optimize.minimize(obj,x0,method='Nelder-Mead',bounds=((0.001,0.999),(0.001,10)))
235 |
236 | assert results.success
237 |
238 | # c. final evaluation
239 | obj(results.x)
--------------------------------------------------------------------------------
/projects/2023/InauguralProject2023.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NumEconCopenhagen/IntroProg-lectures/bfd33808dbfc7bf3e60851b608c50d1bb0fbb5c2/projects/2023/InauguralProject2023.pdf
--------------------------------------------------------------------------------
/projects/DataProject.lyx:
--------------------------------------------------------------------------------
1 | #LyX 2.3 created this file. For more info see http://www.lyx.org/
2 | \lyxformat 544
3 | \begin_document
4 | \begin_header
5 | \save_transient_properties true
6 | \origin unavailable
7 | \textclass article
8 | \begin_preamble
9 | \usepackage{hyperref}
10 | \hypersetup{colorlinks=true,urlcolor=blue}
11 | \date{}
12 | \end_preamble
13 | \use_default_options true
14 | \maintain_unincluded_children false
15 | \language english
16 | \language_package default
17 | \inputencoding auto
18 | \fontencoding global
19 | \font_roman "lmodern" "default"
20 | \font_sans "default" "default"
21 | \font_typewriter "default" "default"
22 | \font_math "auto" "auto"
23 | \font_default_family default
24 | \use_non_tex_fonts false
25 | \font_sc false
26 | \font_osf false
27 | \font_sf_scale 100 100
28 | \font_tt_scale 100 100
29 | \use_microtype false
30 | \use_dash_ligatures false
31 | \graphics default
32 | \default_output_format default
33 | \output_sync 0
34 | \bibtex_command default
35 | \index_command default
36 | \float_placement H
37 | \paperfontsize 11
38 | \spacing single
39 | \use_hyperref false
40 | \papersize default
41 | \use_geometry true
42 | \use_package amsmath 1
43 | \use_package amssymb 1
44 | \use_package cancel 1
45 | \use_package esint 1
46 | \use_package mathdots 0
47 | \use_package mathtools 1
48 | \use_package mhchem 1
49 | \use_package stackrel 1
50 | \use_package stmaryrd 1
51 | \use_package undertilde 1
52 | \cite_engine natbib
53 | \cite_engine_type authoryear
54 | \biblio_style plainnat
55 | \use_bibtopic false
56 | \use_indices false
57 | \paperorientation portrait
58 | \suppress_date false
59 | \justification true
60 | \use_refstyle 0
61 | \use_minted 0
62 | \index Index
63 | \shortcut idx
64 | \color #008000
65 | \end_index
66 | \leftmargin 3cm
67 | \topmargin 3cm
68 | \rightmargin 3cm
69 | \bottommargin 3cm
70 | \secnumdepth 3
71 | \tocdepth 3
72 | \paragraph_separation skip
73 | \defskip smallskip
74 | \is_math_indent 0
75 | \math_numbering_side default
76 | \quotes_style english
77 | \dynamic_quotes 0
78 | \papercolumns 1
79 | \papersides 1
80 | \paperpagestyle empty
81 | \tracking_changes false
82 | \output_changes false
83 | \html_math_output 0
84 | \html_css_as_file 0
85 | \html_be_strict false
86 | \end_header
87 |
88 | \begin_body
89 |
90 | \begin_layout Title
91 |
92 | \shape smallcaps
93 | \size largest
94 | Project 1: Data Analysis Project
95 | \end_layout
96 |
97 | \begin_layout Standard
98 | \begin_inset ERT
99 | status open
100 |
101 | \begin_layout Plain Layout
102 |
103 |
104 | \backslash
105 | vspace{-3mm}
106 | \backslash
107 | thispagestyle{empty}
108 | \end_layout
109 |
110 | \end_inset
111 |
112 |
113 | \series bold
114 | Vision:
115 | \series default
116 | Programming is more than writing code.
117 | The ultimate goal of the projects in this course is that you learn to formulate
118 | a programming problem of your own choice, and find your own way to solve
119 | it, and present the results.
120 | The bullets below are minimum requirements, but otherwise it is very much
121 | up to you, what you will like to do with your project.
122 | Remember to write your code in a manner that is apt for others to run and
123 | read when providing feedback.
124 | We hope to see some creative ideas!
125 | \end_layout
126 |
127 | \begin_layout Itemize
128 |
129 | \series bold
130 | Objectives:
131 | \series default
132 | In your data analysis project, you should show that you can:
133 | \end_layout
134 |
135 | \begin_deeper
136 | \begin_layout Enumerate
137 | Apply data cleaning and data structuring methods.
138 | \end_layout
139 |
140 | \begin_layout Enumerate
141 | Apply data analysis methods.
142 | \end_layout
143 |
144 | \begin_layout Enumerate
145 | Structure a code project.
146 | \end_layout
147 |
148 | \begin_layout Enumerate
149 | Document code.
150 | \end_layout
151 |
152 | \begin_layout Enumerate
153 | Present results in text form and in figures.
154 | \end_layout
155 |
156 | \end_deeper
157 | \begin_layout Itemize
158 |
159 | \series bold
160 | Content:
161 | \series default
162 | Find a subject you are interested in or perhaps have knowledge about already.
163 | For example the subject of your BA thesis (if you are that far), something
164 | related to your student job, or perhaps what you would like write about
165 | in your next thesis or seminar paper.
166 | It can also just be some data you find interesting.
167 | The important thing here is the quality of the code and the presentation
168 | of results, not whether you acquire new economic knowledge.
169 | In your data analysis project, you should at a minimum:
170 | \end_layout
171 |
172 | \begin_deeper
173 | \begin_layout Enumerate
174 | Import data from an online source of your own choosing (through download
175 | or an API).
176 | \end_layout
177 |
178 | \begin_layout Enumerate
179 | Present the data visually (and perhaps interactively).
180 | \end_layout
181 |
182 | \begin_layout Enumerate
183 | Apply some method(s) from descriptive economics (
184 | \begin_inset Quotes ald
185 | \end_inset
186 |
187 | samfundsbeskrivelse
188 | \begin_inset Quotes ard
189 | \end_inset
190 |
191 | ).
192 | That is, make a report that tells a story in numbers and graphs about an
193 | economic phenomenon or trend.
194 | \end_layout
195 |
196 | \begin_layout Standard
197 |
198 | \series bold
199 | Example of structure:
200 | \series default
201 |
202 | \begin_inset CommandInset href
203 | LatexCommand href
204 | name "See this repository"
205 | target "https://github.com/NumEconCopenhagen/IntroProg-example"
206 | literal "false"
207 |
208 | \end_inset
209 |
210 | .
211 | \end_layout
212 |
213 | \end_deeper
214 | \begin_layout Itemize
215 |
216 | \series bold
217 | Structure:
218 | \series default
219 | Your data analysis project should consist of:
220 | \end_layout
221 |
222 | \begin_deeper
223 | \begin_layout Enumerate
224 | A README.md with a short introduction to your project.
225 | It is written in Markdown like a Jupyter Notebook.
226 | \end_layout
227 |
228 | \begin_layout Enumerate
229 | A single self-contained notebook (.ipynb) presenting the analysis.
230 | \end_layout
231 |
232 | \begin_layout Enumerate
233 | Fully documented Python module files (.py).
234 | \end_layout
235 |
236 | \begin_layout Enumerate
237 | Data.
238 | If you do not access data through an API call in your Python scripts, then
239 | you must store it in a .csv file that is uploaded to your repository with
240 | the code files.
241 | Note that Github won't allow you to upload very large data sets (ie.
242 | multiple GBs).
243 | \end_layout
244 |
245 | \end_deeper
246 | \begin_layout Itemize
247 |
248 | \series bold
249 | Size:
250 | \series default
251 | \emph on
252 | Quality before quantity
253 | \emph default
254 | .
255 | Cleaning and joining a couple of data sets and then making some interactive
256 | figures might be just fine.
257 | Else you will be asked to extend it for the exam.
258 | Handing in something that is good but a bit short, is much better than
259 | handing in a bunch of random graphs.
260 | \end_layout
261 |
262 | \begin_layout Itemize
263 |
264 | \series bold
265 | Hand-in:
266 | \series default
267 | On GitHub by uploading it to your repository in the dataproject folder:
268 | \end_layout
269 |
270 | \begin_deeper
271 | \begin_layout Quote
272 | github.com/projects-YEAR-YOURGROUPNAME/dataproject/
273 | \end_layout
274 |
275 | \end_deeper
276 | \begin_layout Itemize
277 |
278 | \series bold
279 | Deadline:
280 | \series default
281 | See
282 | \begin_inset CommandInset href
283 | LatexCommand href
284 | name "Calendar"
285 | target "https://sites.google.com/view/numeconcph-introprog/calendar"
286 | literal "false"
287 |
288 | \end_inset
289 |
290 | .
291 | \end_layout
292 |
293 | \begin_layout Itemize
294 |
295 | \series bold
296 | Peer feedback:
297 | \series default
298 | After handing in, you will be asked to give peer feedback on the projects
299 | of two other groups.
300 | \end_layout
301 |
302 | \begin_layout Itemize
303 |
304 | \series bold
305 | Exam:
306 | \series default
307 | Your data analysis project will be a part of your exam portfolio.
308 | \begin_inset Newline newline
309 | \end_inset
310 |
311 | You can incorporate feedback before handing in the final version.
312 | \end_layout
313 |
314 | \begin_layout Itemize
315 | \begin_inset Formula $\mathbf{Note:}$
316 | \end_inset
317 |
318 | You can find a few suggestions for APIs to use in the notebook about fetching
319 | data.
320 | You are very welcome to find data elsewhere!
321 | \end_layout
322 |
323 | \end_body
324 | \end_document
325 |
--------------------------------------------------------------------------------
/projects/DataProject.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NumEconCopenhagen/IntroProg-lectures/bfd33808dbfc7bf3e60851b608c50d1bb0fbb5c2/projects/DataProject.pdf
--------------------------------------------------------------------------------
/projects/ExamProject.lyx:
--------------------------------------------------------------------------------
1 | #LyX 2.3 created this file. For more info see http://www.lyx.org/
2 | \lyxformat 544
3 | \begin_document
4 | \begin_header
5 | \save_transient_properties true
6 | \origin unavailable
7 | \textclass article
8 | \begin_preamble
9 | \usepackage{hyperref}
10 | \hypersetup{colorlinks=true,urlcolor=blue}
11 | \date{}
12 | \end_preamble
13 | \use_default_options true
14 | \maintain_unincluded_children false
15 | \language english
16 | \language_package default
17 | \inputencoding auto
18 | \fontencoding global
19 | \font_roman "lmodern" "default"
20 | \font_sans "default" "default"
21 | \font_typewriter "default" "default"
22 | \font_math "auto" "auto"
23 | \font_default_family default
24 | \use_non_tex_fonts false
25 | \font_sc false
26 | \font_osf false
27 | \font_sf_scale 100 100
28 | \font_tt_scale 100 100
29 | \use_microtype false
30 | \use_dash_ligatures false
31 | \graphics default
32 | \default_output_format default
33 | \output_sync 0
34 | \bibtex_command default
35 | \index_command default
36 | \float_placement H
37 | \paperfontsize 11
38 | \spacing single
39 | \use_hyperref false
40 | \papersize default
41 | \use_geometry true
42 | \use_package amsmath 1
43 | \use_package amssymb 1
44 | \use_package cancel 1
45 | \use_package esint 1
46 | \use_package mathdots 0
47 | \use_package mathtools 1
48 | \use_package mhchem 1
49 | \use_package stackrel 1
50 | \use_package stmaryrd 1
51 | \use_package undertilde 1
52 | \cite_engine natbib
53 | \cite_engine_type authoryear
54 | \biblio_style plainnat
55 | \use_bibtopic false
56 | \use_indices false
57 | \paperorientation portrait
58 | \suppress_date false
59 | \justification true
60 | \use_refstyle 0
61 | \use_minted 0
62 | \index Index
63 | \shortcut idx
64 | \color #008000
65 | \end_index
66 | \leftmargin 2.7cm
67 | \topmargin 1cm
68 | \rightmargin 2cm
69 | \bottommargin 2.7cm
70 | \secnumdepth 3
71 | \tocdepth 3
72 | \paragraph_separation skip
73 | \defskip smallskip
74 | \is_math_indent 0
75 | \math_numbering_side default
76 | \quotes_style english
77 | \dynamic_quotes 0
78 | \papercolumns 1
79 | \papersides 1
80 | \paperpagestyle empty
81 | \tracking_changes false
82 | \output_changes false
83 | \html_math_output 0
84 | \html_css_as_file 0
85 | \html_be_strict false
86 | \end_header
87 |
88 | \begin_body
89 |
90 | \begin_layout Title
91 |
92 | \shape smallcaps
93 | \size largest
94 | Exam Project
95 | \end_layout
96 |
97 | \begin_layout Standard
98 | \begin_inset ERT
99 | status open
100 |
101 | \begin_layout Plain Layout
102 |
103 |
104 | \backslash
105 | vspace{-3mm}
106 | \backslash
107 | thispagestyle{empty}
108 | \end_layout
109 |
110 | \end_inset
111 |
112 |
113 | \end_layout
114 |
115 | \begin_layout Itemize
116 |
117 | \series bold
118 | Exam project:
119 | \series default
120 | You will be given a notebook with problems similar to those you have encountere
121 | d in the problem sets.
122 | Your objective is to show that you master the tools and algorithms you
123 | have encountered in the course.
124 | The curriculum is the lecture notebooks, with the exception of the advanced
125 | sections marked with an + sign.
126 | \end_layout
127 |
128 | \begin_layout Itemize
129 |
130 | \series bold
131 | Structure:
132 | \series default
133 | Your folders for the data analysis project, the model analysis project,
134 | and the exam project, should each consist of:
135 | \end_layout
136 |
137 | \begin_deeper
138 | \begin_layout Enumerate
139 | A README.md listing any dependencies required to run the code
140 | \end_layout
141 |
142 | \begin_layout Enumerate
143 | A single self-contained notebook (.ipynb) presenting the results
144 | \end_layout
145 |
146 | \begin_layout Enumerate
147 | (Optionally) Fully documented Python files (.py).
148 | \end_layout
149 |
150 | \begin_layout Standard
151 |
152 | \series bold
153 | Example of structure:
154 | \series default
155 |
156 | \begin_inset CommandInset href
157 | LatexCommand href
158 | name "See this repository"
159 | target "https://github.com/NumEconCopenhagen/IntroProg-example"
160 | literal "false"
161 |
162 | \end_inset
163 |
164 | .
165 | \end_layout
166 |
167 | \end_deeper
168 | \begin_layout Itemize
169 |
170 | \series bold
171 | Content:
172 | \series default
173 | In total, you should hand-in a zip-file with the following content:
174 | \end_layout
175 |
176 | \begin_deeper
177 | \begin_layout Enumerate
178 | A general README.md for your portfolio
179 | \end_layout
180 |
181 | \begin_layout Enumerate
182 | Your inaugural project (in the folder
183 | \emph on
184 | /inauguralproject
185 | \emph default
186 | )
187 | \end_layout
188 |
189 | \begin_layout Enumerate
190 | Your data analysis project (in the folder
191 | \emph on
192 | /dataproject
193 | \emph default
194 | )
195 | \end_layout
196 |
197 | \begin_layout Enumerate
198 | Your model analysis project (in the folder
199 | \emph on
200 | /modelproject
201 | \emph default
202 | )
203 | \end_layout
204 |
205 | \begin_layout Enumerate
206 | Your exam project (in the folder
207 | \emph on
208 | /examproject
209 | \emph default
210 | )
211 | \end_layout
212 |
213 | \end_deeper
214 | \begin_layout Itemize
215 |
216 | \series bold
217 | Deadline:
218 | \series default
219 | See the official information.
220 | \end_layout
221 |
222 | \end_body
223 | \end_document
224 |
--------------------------------------------------------------------------------
/projects/ExamProject.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NumEconCopenhagen/IntroProg-lectures/bfd33808dbfc7bf3e60851b608c50d1bb0fbb5c2/projects/ExamProject.pdf
--------------------------------------------------------------------------------
/projects/ExchangeEconomy.py:
--------------------------------------------------------------------------------
1 | from types import SimpleNamespace
2 |
3 | class ExchangeEconomyClass:
4 |
5 | def __init__(self):
6 |
7 | par = self.par = SimpleNamespace()
8 |
9 | # a. preferences
10 | par.alpha = 1/3
11 | par.beta = 2/3
12 |
13 | # b. endowments
14 | par.w1A = 0.8
15 | par.w2A = 0.3
16 |
17 | def utility_A(self,x1A,x2A):
18 | pass
19 |
20 | def utility_B(self,x1B,x2B):
21 | pass
22 |
23 | def demand_A(self,p1):
24 | pass
25 |
26 | def demand_B(self,p1):
27 | pass
28 |
29 | def check_market_clearing(self,p1):
30 |
31 | par = self.par
32 |
33 | x1A,x2A = self.demand_A(p1)
34 | x1B,x2B = self.demand_B(p1)
35 |
36 | eps1 = x1A-par.w1A + x1B-(1-par.w1A)
37 | eps2 = x2A-par.w2A + x2B-(1-par.w2A)
38 |
39 | return eps1,eps2
--------------------------------------------------------------------------------
/projects/InauguralProject2024.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NumEconCopenhagen/IntroProg-lectures/bfd33808dbfc7bf3e60851b608c50d1bb0fbb5c2/projects/InauguralProject2024.pdf
--------------------------------------------------------------------------------
/projects/ModelProject.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NumEconCopenhagen/IntroProg-lectures/bfd33808dbfc7bf3e60851b608c50d1bb0fbb5c2/projects/ModelProject.pdf
--------------------------------------------------------------------------------
/projects/PeerFeedbackGuide.lyx:
--------------------------------------------------------------------------------
1 | #LyX 2.3 created this file. For more info see http://www.lyx.org/
2 | \lyxformat 544
3 | \begin_document
4 | \begin_header
5 | \save_transient_properties true
6 | \origin unavailable
7 | \textclass article
8 | \begin_preamble
9 | \usepackage{hyperref}
10 | \hypersetup{colorlinks=true,urlcolor=blue}
11 | \date{}
12 | \end_preamble
13 | \use_default_options true
14 | \maintain_unincluded_children false
15 | \language english
16 | \language_package default
17 | \inputencoding auto
18 | \fontencoding global
19 | \font_roman "lmodern" "default"
20 | \font_sans "default" "default"
21 | \font_typewriter "default" "default"
22 | \font_math "auto" "auto"
23 | \font_default_family default
24 | \use_non_tex_fonts false
25 | \font_sc false
26 | \font_osf false
27 | \font_sf_scale 100 100
28 | \font_tt_scale 100 100
29 | \use_microtype false
30 | \use_dash_ligatures false
31 | \graphics default
32 | \default_output_format default
33 | \output_sync 0
34 | \bibtex_command default
35 | \index_command default
36 | \float_placement H
37 | \paperfontsize 11
38 | \spacing single
39 | \use_hyperref false
40 | \papersize default
41 | \use_geometry true
42 | \use_package amsmath 1
43 | \use_package amssymb 1
44 | \use_package cancel 1
45 | \use_package esint 1
46 | \use_package mathdots 0
47 | \use_package mathtools 1
48 | \use_package mhchem 1
49 | \use_package stackrel 1
50 | \use_package stmaryrd 1
51 | \use_package undertilde 1
52 | \cite_engine natbib
53 | \cite_engine_type authoryear
54 | \biblio_style plainnat
55 | \use_bibtopic false
56 | \use_indices false
57 | \paperorientation portrait
58 | \suppress_date false
59 | \justification true
60 | \use_refstyle 0
61 | \use_minted 0
62 | \index Index
63 | \shortcut idx
64 | \color #008000
65 | \end_index
66 | \leftmargin 2.7cm
67 | \topmargin 3cm
68 | \rightmargin 2cm
69 | \bottommargin 2.7cm
70 | \secnumdepth 3
71 | \tocdepth 3
72 | \paragraph_separation skip
73 | \defskip smallskip
74 | \is_math_indent 0
75 | \math_numbering_side default
76 | \quotes_style english
77 | \dynamic_quotes 0
78 | \papercolumns 1
79 | \papersides 1
80 | \paperpagestyle empty
81 | \tracking_changes false
82 | \output_changes false
83 | \html_math_output 0
84 | \html_css_as_file 0
85 | \html_be_strict false
86 | \end_header
87 |
88 | \begin_body
89 |
90 | \begin_layout Title
91 |
92 | \shape smallcaps
93 | \size largest
94 | Peer Feedback Guide
95 | \end_layout
96 |
97 | \begin_layout Standard
98 | \begin_inset ERT
99 | status open
100 |
101 | \begin_layout Plain Layout
102 |
103 |
104 | \backslash
105 | thispagestyle{empty}
106 | \end_layout
107 |
108 | \end_inset
109 |
110 |
111 | \end_layout
112 |
113 | \begin_layout Standard
114 |
115 | \series bold
116 | Vision:
117 | \series default
118 | The projects you hand-in should be understandable to your fellow students,
119 | and you should be able to understand other students' projects.
120 | You are therefore required to provide peer feedback on the projects of
121 | two other groups.
122 | Realize that
123 | \series bold
124 |
125 | \series default
126 | the real benefit of this exercise is not that you get some feedback from
127 | others.
128 | The real benefit is that you learn a lot from investigating how others
129 | have attacked a task similar to yours.
130 | \end_layout
131 |
132 | \begin_layout Standard
133 | You must provide the feedback directly on GitHub using the Issues function.
134 | \end_layout
135 |
136 | \begin_layout Standard
137 | The feedback should focus on: Is it straightforward to run the code? Is
138 | it easy to understand what the code is doing? Are the results clearly explained
139 | ? The issues should follow the template below and all comments should be
140 | as constructive as possible:
141 | \end_layout
142 |
143 | \begin_layout Enumerate
144 |
145 | \series bold
146 | The most elegant solution in the project was:
147 | \series default
148 | (explain what and why)
149 | \end_layout
150 |
151 | \begin_layout Enumerate
152 |
153 | \series bold
154 | The hardest section of code in the project to understand was:
155 | \series default
156 | (explain what)
157 | \end_layout
158 |
159 | \begin_layout Enumerate
160 |
161 | \series bold
162 | This part of the project could be better documented:
163 | \series default
164 | (explain what)
165 | \end_layout
166 |
167 | \begin_layout Enumerate
168 |
169 | \series bold
170 | An idea for an improvement/clarification could be:
171 | \series default
172 | (explain what and why)
173 | \end_layout
174 |
175 | \begin_layout Enumerate
176 |
177 | \series bold
178 | An idea for an extension could be:
179 | \series default
180 | (explain what and why)
181 | \end_layout
182 |
183 | \begin_layout Standard
184 |
185 | \series bold
186 | Specific guidelines:
187 | \end_layout
188 |
189 | \begin_layout Enumerate
190 | The assignment of peers to teams is randomized, and will different for the
191 | inaugural, data and model project.
192 | \end_layout
193 |
194 | \begin_layout Enumerate
195 | Find the two groups for whom you need to provide peer feedback here:
196 | \end_layout
197 |
198 | \begin_deeper
199 | \begin_layout Standard
200 | \begin_inset ERT
201 | status open
202 |
203 | \begin_layout Plain Layout
204 |
205 |
206 | \backslash
207 | url{https://docs.google.com/spreadsheets/d/1Bdg7pGXWVCoRVBYZOgzccUMIqmttagraXd52B4
208 | 1Hwko/edit?usp=sharing}
209 | \end_layout
210 |
211 | \end_inset
212 |
213 |
214 | \end_layout
215 |
216 | \end_deeper
217 | \begin_layout Enumerate
218 | Go to their repositories:
219 | \begin_inset Newline newline
220 | \end_inset
221 |
222 |
223 | \begin_inset ERT
224 | status open
225 |
226 | \begin_layout Plain Layout
227 |
228 |
229 | \backslash
230 | url{https://github.com/NumEconCopenhagen/projects-YEAR-GROUPNAME}
231 | \end_layout
232 |
233 | \end_inset
234 |
235 |
236 | \end_layout
237 |
238 | \begin_deeper
239 | \begin_layout Standard
240 | Otherwise, try to search for the groups here:
241 | \begin_inset Newline newline
242 | \end_inset
243 |
244 |
245 | \begin_inset ERT
246 | status open
247 |
248 | \begin_layout Plain Layout
249 |
250 |
251 | \backslash
252 | url{https://github.com/NumEconCopenhagen}
253 | \end_layout
254 |
255 | \end_inset
256 |
257 |
258 | \end_layout
259 |
260 | \end_deeper
261 | \begin_layout Enumerate
262 | Clone their repositories (download them to your own computer).
263 | \end_layout
264 |
265 | \begin_layout Enumerate
266 | Try to run their notebooks.
267 | \end_layout
268 |
269 | \begin_layout Enumerate
270 | Create a new issue in their repositories (click the
271 | \begin_inset Quotes eld
272 | \end_inset
273 |
274 | Issues
275 | \begin_inset Quotes erd
276 | \end_inset
277 |
278 | tab) where you provide the feedback.
279 | \end_layout
280 |
281 | \end_body
282 | \end_document
283 |
--------------------------------------------------------------------------------
/projects/PeerFeedbackGuide.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NumEconCopenhagen/IntroProg-lectures/bfd33808dbfc7bf3e60851b608c50d1bb0fbb5c2/projects/PeerFeedbackGuide.pdf
--------------------------------------------------------------------------------