├── .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 --------------------------------------------------------------------------------