8 |
--------------------------------------------------------------------------------
/source/rst/about_lectures.rst:
--------------------------------------------------------------------------------
1 | **************
2 | About Lectures
3 | **************
4 |
5 | This is one of a series of online texts on modern quantitative
6 | economics and programming with Python. This is the first
7 | text in the series, which focuses on programming in Python.
8 |
9 | For an overview of the series, see `this page `__
--------------------------------------------------------------------------------
/source/rst/search.rst:
--------------------------------------------------------------------------------
1 | .. _search:
2 |
3 | **********
4 | Search
5 | **********
6 |
7 | .. raw:: html
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/scripts/linkchecker-test.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | MODIFIED_FILES="$1"
4 |
5 | RST_FILES=""
6 | for F in $MODIFIED_FILES
7 | do
8 | if [[ $F == *.rst ]]
9 | then
10 | RST_FILES="$RST_FILES $F"
11 | fi
12 | done
13 | echo "List of Changed RST Files: $RST_FILES"
14 | if [ -z "$RST_FILES" ]; then
15 | echo "No RST Files have changed -- nothing to do in this PR"
16 | else
17 | RST_FILES="$RST_FILES source/rst/index_toc.rst"
18 | make linkcheck FILES="$RST_FILES"
19 | fi
--------------------------------------------------------------------------------
/source/rst/index_advanced_python_programming.rst:
--------------------------------------------------------------------------------
1 | .. include:: /_static/includes/header.raw
2 |
3 | ************************************
4 | Advanced Python Programming
5 | ************************************
6 |
7 | This part provides a look at more advanced concepts in Python programming
8 |
9 | .. only:: html
10 |
11 | Lectures
12 | ********
13 |
14 |
15 | .. toctree::
16 | :maxdepth: 2
17 |
18 | writing_good_code
19 | python_advanced_features
20 | debugging
21 |
--------------------------------------------------------------------------------
/source/rst/index_python_scientific_libraries.rst:
--------------------------------------------------------------------------------
1 | .. _learning_python:
2 |
3 | .. include:: /_static/includes/header.raw
4 |
5 | *********************************
6 | The Scientific Libraries
7 | *********************************
8 |
9 | Next we cover the third party libraries most useful for scientific work in Python
10 |
11 |
12 | .. only:: html
13 |
14 | Lectures
15 | ********
16 |
17 |
18 | .. toctree::
19 | :maxdepth: 2
20 |
21 | need_for_speed
22 | numpy
23 | matplotlib
24 | scipy
25 | numba
26 | parallelization
27 | pandas
28 |
--------------------------------------------------------------------------------
/source/rst/index_learning_python.rst:
--------------------------------------------------------------------------------
1 | .. _learning_python:
2 |
3 | .. include:: /_static/includes/header.raw
4 |
5 | **********************
6 | Introduction to Python
7 | **********************
8 |
9 | This first part of the course provides a relatively fast-paced introduction to the Python programming language
10 |
11 |
12 | .. only:: html
13 |
14 | Lectures
15 | ********
16 |
17 |
18 | .. toctree::
19 | :maxdepth: 2
20 |
21 | about_py
22 | getting_started
23 | python_by_example
24 | functions
25 | python_essentials
26 | oop_intro
27 | python_oop
28 |
--------------------------------------------------------------------------------
/scripts/travis-coverage.sh:
--------------------------------------------------------------------------------
1 | echo "PR: $TRAVIS_PULL_REQUEST"
2 | echo "COMMIT RANGE: $TRAVIS_COMMIT_RANGE"
3 | CHANGED_FILES=$(git diff --name-only $TRAVIS_COMMIT_RANGE | grep '\.rst' | tr '\n' ' ')
4 | #Check for Full Deletions
5 | SPHINX_FILES=""
6 | for f in $CHANGED_FILES
7 | do
8 | if [ -f $f ]
9 | then
10 | SPHINX_FILES="$SPHINX_FILES $f"
11 | fi
12 | done
13 | echo "List of Changed Files: $SPHINX_FILES"
14 | if [ -z "$SPHINX_FILES" ]; then
15 | echo "No RST Files have changed -- nothing to do in this PR"
16 | else
17 | make coverage FILES="$SPHINX_FILES"
18 | make linkcheck FILES="$SPHINX_FILES"
19 | fi
--------------------------------------------------------------------------------
/theme/minimal/templates/error_report_template.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Jupyter Script Test Execution - Error Report
4 |
5 |
6 |
7 |
8 |
9 |
10 |
Error Report - Generated {DATETIME}
11 |
12 |
13 | Overview
14 |
15 |
16 | {ERROR_SUMMARY}
17 |
18 | {NOTEBOOK_LOOP}
19 |
20 |
21 |
--------------------------------------------------------------------------------
/source/rst/404.rst:
--------------------------------------------------------------------------------
1 | .. _404:
2 |
3 | ***************
4 | Page Not Found
5 | ***************
6 |
7 | .. raw:: html
8 |
9 |
10 |
11 |
12 | We couldn’t find the page you were looking for.
13 |
14 | Please check the URL or try a link below:
15 |
16 | * `Home >`_
17 | * `QuantEcon `_
18 | * `Quantitative Economics with Python `_
19 | * `Quantitative Economics with Julia `_
20 | * `QuantEcon DataScience `_
21 | * `Forum `_
22 | * `Contact us `_
--------------------------------------------------------------------------------
/source/_static/lecture_specific/pandas/wb_download.py:
--------------------------------------------------------------------------------
1 | import matplotlib.pyplot as plt
2 | import requests
3 | import pandas as pd
4 |
5 | # == Get data and read into file gd.xls == #
6 | wb_data_query = "http://api.worldbank.org/v2/en/indicator/gc.dod.totl.gd.zs?downloadformat=excel"
7 | r = requests.get(wb_data_query)
8 | with open('gd.xls', 'wb') as output:
9 | output.write(r.content)
10 |
11 | # == Parse data into a DataFrame == #
12 | govt_debt = pd.read_excel('gd.xls', sheet_name='Data', skiprows=3, index_col=1)
13 |
14 | # == Take desired values and plot == #
15 | govt_debt = govt_debt.transpose()
16 | govt_debt = govt_debt[['AUS', 'USA']]
17 | govt_debt = govt_debt[38:]
18 | govt_debt.plot(lw=2)
19 | plt.show()
20 |
--------------------------------------------------------------------------------
/source/rst/status.rst:
--------------------------------------------------------------------------------
1 | .. _status:
2 |
3 | **************
4 | Lecture Status
5 | **************
6 |
7 | .. raw:: html
8 |
9 |
10 |
The badges below show which lectures are currently passing their execution test (i.e., executing without errors).
11 |
The lecture code checker was last run: N/A
12 |
13 |
14 |
15 |
The code checker is run on a t2.small Amazon EC2 instance. This is an instance with a single CPU and 2 GiB of Memory.
16 |
You should achieve faster run times on many common laptops and desktops.
--------------------------------------------------------------------------------
/source/_static/lecture_specific/pandas/data/test_pwt.csv:
--------------------------------------------------------------------------------
1 | "country","country isocode","year","POP","XRAT","tcgdp","cc","cg"
2 | "Argentina","ARG","2000","37335.653","0.9995","295072.21869","75.716805379","5.5788042896"
3 | "Australia","AUS","2000","19053.186","1.72483","541804.6521","67.759025993","6.7200975332"
4 | "India","IND","2000","1006300.297","44.9416","1728144.3748","64.575551328","14.072205773"
5 | "Israel","ISR","2000","6114.57","4.07733","129253.89423","64.436450847","10.266688415"
6 | "Malawi","MWI","2000","11801.505","59.543808333","5026.2217836","74.707624181","11.658954494"
7 | "South Africa","ZAF","2000","45064.098","6.93983","227242.36949","72.718710427","5.7265463933"
8 | "United States","USA","2000","282171.957","1","9898700","72.347054303","6.0324539789"
9 | "Uruguay","URY","2000","3219.793","12.099591667","25255.961693","78.978740282","5.108067988"
10 |
--------------------------------------------------------------------------------
/scripts/execution-test.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | CLEAN_BUILD=false
4 | MODIFIED_FILES="$1"
5 |
6 | RST_FILES=""
7 | for F in $MODIFIED_FILES
8 | do
9 | if [[ $F == environment.yml ]]
10 | then
11 | CLEAN_BUILD=true
12 | break
13 | fi
14 | #Extract List of RST Files
15 | if [[ $F == *.rst ]]
16 | then
17 | RST_FILES="$RST_FILES $F"
18 | fi
19 | done
20 |
21 | echo "List of Changed RST Files: $RST_FILES"
22 | echo "Clean Build Requested: $CLEAN_BUILD"
23 |
24 | if [ "$CLEAN_BUILD" = true ]
25 | then
26 | echo "Running Clean Build"
27 | make coverage
28 | elif [ -z "$RST_FILES" ]
29 | then
30 | echo "No RST Files have changed -- nothing to do in this PR"
31 | else
32 | RST_FILES="$RST_FILES source/rst/index_toc.rst"
33 | echo "Running Selecting Build with: $RST_FILES"
34 | make coverage FILES="$RST_FILES"
35 | fi
--------------------------------------------------------------------------------
/scripts/build-website.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | MODIFIED_FILES="$1"
4 | PRIVATE_THEME=$2
5 |
6 | RST_FILES=""
7 | for F in $MODIFIED_FILES
8 | do
9 | if [[ $F == *.rst ]]
10 | then
11 | RST_FILES="$RST_FILES $F"
12 | fi
13 | done
14 | echo "List of Changed RST Files: $RST_FILES"
15 | echo "Building with Private theme: $PRIVATE_THEME"
16 | if [ -z "$RST_FILES" ]; then
17 | echo "::set-env name=BUILD_NETLIFY::false"
18 | echo "No RST Files have changed -- nothing to do in this PR"
19 | else
20 | echo "::set-env name=BUILD_NETLIFY::true"
21 | RST_FILES="$RST_FILES source/rst/index_toc.rst"
22 | if [ "$PRIVATE_THEME" = true ]; then
23 | make website THEMEPATH=theme/lecture-python-programming.theme FILES="$RST_FILES"
24 | else
25 | make website FILES="$RST_FILES"
26 | fi
27 | ls _build/website/jupyter_html/* #Ensure build files are created
28 | fi
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: Execution and Link Checks
2 | on: [pull_request]
3 | jobs:
4 | tests:
5 | runs-on: ubuntu-latest
6 | steps:
7 | - name: Checkout
8 | uses: actions/checkout@v2
9 | - name: Setup Anaconda
10 | uses: goanpeca/setup-miniconda@v1
11 | with:
12 | auto-update-conda: true
13 | auto-activate-base: true
14 | miniconda-version: 'latest'
15 | python-version: 3.7
16 | environment-file: environment.yml
17 | activate-environment: qe-lectures
18 | - name: Get Changed Files
19 | id: files
20 | uses: jitterbit/get-changed-files@v1
21 | - name: Run Execution Tests
22 | shell: bash -l {0}
23 | run: bash scripts/execution-test.sh "${{ steps.files.outputs.added_modified }}"
24 | - name: Run Linkchecker
25 | shell: bash -l {0}
26 | run: bash scripts/linkchecker-test.sh "${{ steps.files.outputs.added_modified }}"
--------------------------------------------------------------------------------
/theme/minimal/static/img/powered-by-NumFOCUS-orange.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/source/rst/index_toc.rst:
--------------------------------------------------------------------------------
1 | .. _toc:
2 |
3 | .. raw:: html
4 |
5 |
6 |
7 | .. only:: html
8 |
9 | Table of Contents
10 | *****************
11 |
12 | .. toctree::
13 | :maxdepth: 2
14 | :titlesonly:
15 |
16 | about_lectures
17 | index_learning_python
18 | index_python_scientific_libraries
19 | index_advanced_python_programming
20 |
21 |
22 | .. toctree::
23 | :hidden:
24 |
25 | 404
26 | search
27 | status
28 | troubleshooting
29 |
30 | |
31 |
32 | .. image:: http://assets.quantecon.org/img/banner.png
33 | :scale: 30%
34 | :align: center
35 |
36 | .. only:: latex
37 |
38 | Acknowledgements: These lectures have benefitted greatly from comments and
39 | suggestion from our colleagues, students and friends. Special thanks go to
40 | Anmol Bhandari, Long Bui, Jeong-Hun Choi, Chase Coleman, David Evans, Shunsuke Hori,
41 | Chenghan Hou, Doc-Jin Jang, Spencer Lyon, Qingyin Ma, Akira Matsushita,
42 | Matthew McKay, Tomohito Okabe, Alex Olssen, Nathan Palmer and Yixiao Zhou.
43 |
--------------------------------------------------------------------------------
/.github/workflows/coverage.yml:
--------------------------------------------------------------------------------
1 | name: Execution and Link Testing (Nightly)
2 | on:
3 | schedule:
4 | - cron: '0 17 * * *'
5 | jobs:
6 | coverage:
7 | name: Run Coverage
8 | runs-on: ubuntu-latest
9 | steps:
10 | - name: Checkout
11 | uses: actions/checkout@v2
12 | - name: Setup Anaconda
13 | uses: goanpeca/setup-miniconda@v1
14 | with:
15 | auto-update-conda: true
16 | auto-activate-base: true
17 | miniconda-version: 'latest'
18 | python-version: 3.7
19 | environment-file: environment.yml
20 | activate-environment: qe-lectures
21 | - name: Run Execution Tests
22 | shell: bash -l {0}
23 | run: make coverage
24 | linkchecker:
25 | name: Run linkchecker
26 | runs-on: ubuntu-latest
27 | steps:
28 | - name: Checkout
29 | uses: actions/checkout@v2
30 | - name: Setup Anaconda
31 | uses: goanpeca/setup-miniconda@v1
32 | with:
33 | auto-update-conda: true
34 | auto-activate-base: true
35 | miniconda-version: 'latest'
36 | python-version: 3.7
37 | environment-file: environment.yml
38 | activate-environment: qe-lectures
39 | - name: Run Linkchecker
40 | shell: bash -l {0}
41 | run: make linkcheck
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | BSD 3-Clause License
2 |
3 | Copyright (c) 2020, QuantEcon
4 | All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | * Redistributions of source code must retain the above copyright notice, this
10 | list of conditions and the following disclaimer.
11 |
12 | * Redistributions in binary form must reproduce the above copyright notice,
13 | this list of conditions and the following disclaimer in the documentation
14 | and/or other materials provided with the distribution.
15 |
16 | * Neither the name of the copyright holder nor the names of its
17 | contributors may be used to endorse or promote products derived from
18 | this software without specific prior written permission.
19 |
20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 |
--------------------------------------------------------------------------------
/theme/minimal/static/css/qe.python.css:
--------------------------------------------------------------------------------
1 | /* Homepage */
2 | .home-intro {
3 | display: flex;
4 | align-content: center;
5 | }
6 | .home-blurb {
7 | font-size: 1.1rem;
8 | line-height: 1.5;
9 | }
10 | .home-intro .sponsor {
11 | list-style: none;
12 | padding:0;
13 | flex-shrink: 0;
14 | margin:0 60px 0 4rem;
15 | text-align: center;
16 | }
17 | .home-intro .sponsor li {
18 | display: block;
19 | margin:1rem 0;
20 | padding:0;
21 | }
22 | .web-version {
23 | display:inline-block;
24 | padding: 2rem 0rem;
25 | }
26 | .web-version a {
27 | display: block;
28 | padding:1rem 40px 1rem 80px;
29 | position: relative;
30 | }
31 | .web-version a .thumb {
32 | position: absolute;
33 | left:0px;
34 | top:1rem;
35 | }
36 | .web-version a .thumb img {
37 | width:50px;
38 | }
39 | .web-version a h2 {
40 | line-height: 1;
41 | margin:0;
42 | font-size: 1.4rem;
43 | }
44 | .web-version a p {
45 | margin:10px 0 0 0;
46 | }
47 | .home-alternatives {
48 | padding: 1rem 0rem;
49 | }
50 | .home-alternatives ul {
51 | list-style: none;
52 | padding:0;
53 | margin:0 0;
54 | }
55 | .home-alternatives li {
56 | padding:0;
57 | margin:1rem 1rem;
58 | }
59 | .home-alternatives li a {
60 | display: block;
61 | }
62 | .home-alternatives li a h3 {
63 | line-height: 1;
64 | margin:0;
65 | font-size: 1.2rem;
66 | }
67 | .home-alternatives li a p {
68 | margin:10px 0 0 0;
69 | }
70 | @media only screen and (max-width: 768px) {
71 | .home-intro {
72 | display: block;
73 | }
74 | .home-intro .sponsor {
75 | margin:0 auto;
76 | }
77 | }
78 |
79 |
80 | /* Other */
81 | #qe-notebook-header {
82 | display: none;
83 | }
--------------------------------------------------------------------------------
/.github/workflows/deploy-s3.yml:
--------------------------------------------------------------------------------
1 | # name: Build Website and Deploy to S3
2 | # on:
3 | # push:
4 | # branches:
5 | # - master
6 | # jobs:
7 | # build-cache:
8 | # name: Build Website
9 | # runs-on: ubuntu-latest
10 | # steps:
11 | # - name: Checkout
12 | # uses: actions/checkout@v2
13 | # - name: Setup Anaconda
14 | # uses: goanpeca/setup-miniconda@v1
15 | # with:
16 | # auto-update-conda: true
17 | # auto-activate-base: true
18 | # miniconda-version: 'latest'
19 | # python-version: 3.7
20 | # environment-file: environment.yml
21 | # activate-environment: qe-lectures
22 | # - name: Checkout QuantEcon theme
23 | # uses: actions/checkout@v2
24 | # with:
25 | # repository: QuantEcon/lecture-python-programming.theme
26 | # token: ${{ secrets.ACTIONS_PAT }}
27 | # path: theme/lecture-python-programming.theme
28 | # - name: Build Website files
29 | # shell: bash -l {0}
30 | # run: |
31 | # make website THEMEPATH=theme/lecture-python-programming.theme
32 | # ls _build/website/jupyter_html/*
33 | # - name: Configure AWS credentials
34 | # uses: aws-actions/configure-aws-credentials@v1
35 | # with:
36 | # aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
37 | # aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
38 | # aws-region: ap-southeast-2
39 | # - name: Copy files to S3 with the AWS CLI
40 | # run: |
41 | # aws s3 sync _build/website/jupyter_html/ s3://test.python.quantecon.org/
--------------------------------------------------------------------------------
/source/_static/lecture_specific/about_py/qs.py:
--------------------------------------------------------------------------------
1 |
2 | import matplotlib.pyplot as plt
3 | import numpy as np
4 | from scipy.stats import norm
5 | from matplotlib import cm
6 |
7 | xmin, xmax = -4, 12
8 | x = 10
9 | α = 0.5
10 |
11 | m, v = x, 10
12 |
13 | xgrid = np.linspace(xmin, xmax, 200)
14 |
15 | fig, ax = plt.subplots()
16 |
17 | ax.spines['right'].set_color('none')
18 | ax.spines['top'].set_color('none')
19 | ax.spines['left'].set_color('none')
20 | ax.xaxis.set_ticks_position('bottom')
21 | ax.spines['bottom'].set_position(('data', 0))
22 |
23 | ax.set_ylim(-0.05, 0.5)
24 | ax.set_xticks((x,))
25 | ax.set_xticklabels((r'$x$', ), fontsize=18)
26 | ax.set_yticks(())
27 |
28 | K = 3
29 | for i in range(K):
30 | m = α * m
31 | v = α * α * v + 1
32 | f = norm(loc=m, scale=np.sqrt(v))
33 | k = (i + 0.5) / K
34 | ax.plot(xgrid, f.pdf(xgrid), lw=1, color='black', alpha=0.4)
35 | ax.fill_between(xgrid, 0 * xgrid, f.pdf(xgrid), color=cm.jet(k), alpha=0.4)
36 |
37 |
38 | ax.annotate(r'$Q(x,\cdot)$', xy=(6.6, 0.2), xycoords='data',
39 | xytext=(20, 90), textcoords='offset points', fontsize=16,
40 | arrowprops=dict(arrowstyle="->", connectionstyle="arc3,rad=-0.2"))
41 | ax.annotate(r'$Q^2(x,\cdot)$', xy=(3.6, 0.24), xycoords='data',
42 | xytext=(20, 90), textcoords='offset points', fontsize=16,
43 | arrowprops=dict(arrowstyle="->", connectionstyle="arc3,rad=-0.2"))
44 | ax.annotate(r'$Q^3(x,\cdot)$', xy=(-0.2, 0.28), xycoords='data',
45 | xytext=(-90, 90), textcoords='offset points', fontsize=16,
46 | arrowprops=dict(arrowstyle="->", connectionstyle="arc3,rad=0.2"))
47 | fig.show()
48 |
--------------------------------------------------------------------------------
/.github/workflows/preview.yml:
--------------------------------------------------------------------------------
1 | name: 'Netlify Preview Deploy'
2 | on:
3 | pull_request:
4 | types: ['opened', 'edited', 'synchronize']
5 | jobs:
6 | deploy-preview:
7 | name: 'Deploy'
8 | runs-on: ubuntu-latest
9 | steps:
10 | - name: Checkout
11 | uses: actions/checkout@v2
12 | - name: Setup Anaconda
13 | uses: goanpeca/setup-miniconda@v1
14 | with:
15 | auto-update-conda: true
16 | auto-activate-base: true
17 | miniconda-version: 'latest'
18 | python-version: 3.7
19 | environment-file: environment.yml
20 | activate-environment: qe-lectures
21 | - name: Get Changed Files
22 | id: files
23 | uses: jitterbit/get-changed-files@v1
24 | - name: Checkout QuantEcon theme
25 | if: github.event.pull_request.head.repo.full_name == github.repository
26 | uses: actions/checkout@v2
27 | with:
28 | repository: QuantEcon/lecture-python-programming.theme
29 | token: ${{ secrets.ACTIONS_PAT }}
30 | path: theme/lecture-python-programming.theme
31 | # - name: Get current date
32 | # id: date
33 | # run: echo "::set-output name=date::$(date +'%Y-%m-%d')"
34 | - name: Check Sphinx Cache
35 | id: cache
36 | uses: actions/cache@v1
37 | with:
38 | path: _build
39 | key: cache-sphinx
40 | # key: cache-sphinx-${{ steps.date.outputs.date }}
41 | - name: Build website files
42 | shell: bash -l {0}
43 | run: |
44 | bash scripts/build-website.sh "${{ steps.files.outputs.added_modified }}" "${{ github.event.pull_request.head.repo.full_name == github.repository }}"
45 | - name: Preview Deploy to Netlify
46 | uses: nwtgck/actions-netlify@v1.1
47 | if: env.BUILD_NETLIFY == 'true'
48 | with:
49 | publish-dir: './_build/website/jupyter_html'
50 | production-branch: master
51 | github-token: ${{ secrets.GITHUB_TOKEN }}
52 | deploy-message: "Preview Deploy from GitHub Actions"
53 | env:
54 | NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
55 | NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}
--------------------------------------------------------------------------------
/source/rst/troubleshooting.rst:
--------------------------------------------------------------------------------
1 | .. _troubleshooting:
2 |
3 | .. include:: /_static/includes/header.raw
4 |
5 | .. highlight:: python3
6 |
7 | ***************
8 | Troubleshooting
9 | ***************
10 |
11 | .. contents:: :depth: 2
12 |
13 | This page is for readers experiencing errors when running the code from the lectures.
14 |
15 | Fixing Your Local Environment
16 | ==============================
17 |
18 | The basic assumption of the lectures is that code in a lecture should execute whenever
19 |
20 | #. it is executed in a Jupyter notebook and
21 |
22 | #. the notebook is running on a machine with the latest version of Anaconda Python.
23 |
24 | You have installed Anaconda, haven't you, following the instructions in :doc:`this lecture `?
25 |
26 | Assuming that you have, the most common source of problems for our readers is that their Anaconda distribution is not up to date.
27 |
28 | `Here's a useful article `__
29 | on how to update Anaconda.
30 |
31 | Another option is to simply remove Anaconda and reinstall.
32 |
33 | You also need to keep the external code libraries, such as `QuantEcon.py
34 | `__ up to date.
35 |
36 | For this task you can either
37 |
38 | * use `conda upgrade quantecon` on the command line, or
39 |
40 | * execute `!conda upgrade quantecon` within a Jupyter notebook.
41 |
42 | If your local environment is still not working you can do two things.
43 |
44 | First, you can use a remote machine instead, by clicking on the `Launch Notebook` icon available for each lecture
45 |
46 | .. image:: _static/lecture_specific/troubleshooting/launch.png
47 |
48 | Second, you can report an issue, so we can try to fix your local set up.
49 |
50 | We like getting feedback on the lectures so please don't hesitate to get in
51 | touch.
52 |
53 | Reporting an Issue
54 | ===================
55 |
56 | One way to give feedback is to raise an issue through our `issue tracker
57 | `__.
58 |
59 | Please be as specific as possible. Tell us where the problem is and as much
60 | detail about your local set up as you can provide.
61 |
62 | Another feedback option is to use our `discourse forum `__.
63 |
64 | Finally, you can provide direct feedback to contact@quantecon.org
65 |
66 |
--------------------------------------------------------------------------------
/source/rst/index.rst:
--------------------------------------------------------------------------------
1 | .. _index:
2 |
3 | **********************************************
4 | Python Programming for Economics and Finance
5 | **********************************************
6 |
7 | .. toctree::
8 | :hidden:
9 |
10 | index_toc
11 |
12 |
13 | .. raw:: html
14 |
15 |
16 |
Python Programming for Economics and Finance
17 |
18 |
19 |
20 |
This website presents a set of lectures on Python programming for economics and finance, designed and written by Thomas J. Sargent and John Stachurski.
";
118 | table.innerHTML = rawHTML;
119 | // add the data
120 | for (var i = 0; i < status_data.length; i ++)
121 | {
122 | var table = document.getElementById("status_table");
123 | var row = table.insertRow(-1);
124 | row.setAttribute("id", status_data[i]['name'], 0);
125 |
126 | // Insert new cells (
elements) at the 1st and 2nd position of the "new"
element:
127 | var lectureCell = row.insertCell(0);
128 | var langCell = row.insertCell(1);
129 | var runtimeCell = row.insertCell(2);
130 | var statusCell = row.insertCell(3);
131 | var badge, status, color, lang, link;
132 |
133 | if (status_data[i]['result'] === 0)
134 | {
135 | status = "Passing";
136 | color = "brightgreen";
137 | }
138 | else if (status_data[i]['result'] === 1)
139 | {
140 | status = "Failing";
141 | color = "red";
142 | }
143 | else if (status_data[i]['result'] === -1) {
144 | status = "Not available";
145 | color = "lightgrey";
146 | }
147 |
148 | link = '/' + status_data[i]['name'] + '.html';
149 |
150 | badge = '';
151 |
152 | // Add some text to the new cells:
153 | lectureCell.innerHTML = status_data[i]['name'];
154 | langCell.innerHTML = status_data[i]['language'];
155 | runtimeCell.innerHTML = status_data[i]['runtime'];
156 | statusCell.innerHTML = badge;
157 |
158 |
159 | }
160 | })
161 | }
162 |
163 |
164 | // Show executability status badge in header
165 |
166 | const LECTURE_OK = 0;
167 | const LECTURE_FAILED = 1;
168 | const LECTURE_ERROR = -1;
169 |
170 | function update_page_badge(page_status)
171 | {
172 | var badge = document.getElementById("executability_status_badge");
173 | var status, color;
174 |
175 | if (page_status === LECTURE_OK)
176 | {
177 | status = "Passing";
178 | color = "brightgreen";
179 | }
180 | else if (page_status == LECTURE_FAILED)
181 | {
182 | status = "Failing";
183 | color = "red";
184 | }
185 | else if (page_status == LECTURE_ERROR)
186 | {
187 | status = "Not available";
188 | color = "lightgrey";
189 | }
190 | else
191 | {
192 | console.log("Panic! Invalid parameter passed to update_page_badge().");
193 | }
194 |
195 | badge.innerHTML = '';
196 |
197 | //badge.style.display="block";
198 |
199 | return;
200 | }
201 |
202 | function determine_page_status(status_data)
203 | {
204 | var path = window.location.pathname;
205 | var filename_parts = path.split("/");
206 | var filename = filename_parts.pop();
207 |
208 | var lecture_name = filename.split(".")[0].toLowerCase();
209 |
210 | var res = LECTURE_ERROR;
211 |
212 | for (var i = 0; i < status_data.length; i ++)
213 | {
214 | if (status_data[i]['name'].split('/').pop() === lecture_name)
215 | {
216 | if (status_data[i]['result'] === 0)
217 | {
218 | res = LECTURE_OK;
219 | }
220 | else
221 | {
222 | res = LECTURE_FAILED;
223 | }
224 | }
225 | }
226 | return res;
227 | }
228 |
229 | function load_this_page_badge()
230 | {
231 | loadCodeExecutionJSON(function(response) {
232 | // Parsing JSON string into object
233 | var data = JSON.parse(response);
234 | status_data = [];
235 | for (var key in data.results)
236 | {
237 | var new_record = {};
238 | new_record['name'] = data.results[key].filename;
239 | new_record['runtime'] = data.results[key].runtime;
240 | new_record['extension'] = data.results[key].extension;
241 | new_record['result'] = data.results[key].num_errors;
242 | new_record['language'] = data.results[key].language;
243 | status_data.push(new_record);
244 | }
245 | var page_status = determine_page_status(status_data);
246 | update_page_badge(page_status);
247 | });
248 | }
249 |
250 | function get_badge(percentage)
251 | {
252 | var color, badge;
253 |
254 | if (percentage > -1)
255 | {
256 | if ( percentage < 50 ) {
257 | color = 'red';
258 | } else {
259 | color = 'brightgreen';
260 | }
261 | badge = 'https://img.shields.io/badge/Total%20coverage-' + percentage + '%25-' + color + '.svg';
262 | } else {
263 | badge = 'https://img.shields.io/badge/Total%20coverage-not%20available-lightgrey.svg>';
264 | }
265 | return badge;
266 | }
267 |
268 | function load_percentages()
269 | {
270 | var number_of_lectures = {};
271 | var number_which_passed = {};
272 | var keys_list = [];
273 | var combined_percentage;
274 |
275 | loadCodeExecutionJSON(function(response) {
276 | // Parsing JSON string into object
277 | var data = JSON.parse(response);
278 | for (var key in data.results)
279 | {
280 | if (data.results[key].num_errors === 0)
281 | {
282 | if (!(data.results[key].extension in number_which_passed))
283 | {
284 | number_which_passed[data.results[key].extension] = 0;
285 | keys_list.push(data.results[key].extension);
286 | }
287 | number_which_passed[data.results[key].extension] += 1;
288 | }
289 |
290 | if (!(data.results[key].extension in number_of_lectures))
291 | {
292 | number_of_lectures[data.results[key].extension] = 0;
293 | }
294 | number_of_lectures[data.results[key].extension] += 1;
295 | }
296 |
297 | var percentages = {};
298 | var total_lectures = 0;
299 | var total_passing = 0;
300 | for (var k in keys_list)
301 | {
302 | key = keys_list[k];
303 |
304 | percentages[key] = 0;
305 | if (number_of_lectures[key] === 0)
306 | {
307 | // An appropriate value for this is yet to be determined.
308 | percentages[key] = 100;
309 | }
310 | else
311 | {
312 | percentages[key] = Math.floor(100 * number_which_passed[key] / number_of_lectures[key]);
313 | }
314 |
315 | // Sensible boundary checking.
316 | if (percentages[key] < 0 || percentages[key] > 100)
317 | {
318 | percentages[key] = -1;
319 | }
320 |
321 | total_lectures += number_of_lectures[key];
322 | total_passing += number_which_passed[key];
323 | }
324 |
325 | if (total_lectures === 0)
326 | {
327 | combined_percentage = 0;
328 | }
329 | else
330 | {
331 | combined_percentage = Math.floor(100 * total_passing / total_lectures);
332 | }
333 |
334 | var badge = document.getElementById("coverage_badge");
335 | badge.innerHTML = '';
336 |
337 | });
338 |
339 | }
340 |
341 | if ( document.getElementById('executability_status_badge') ) {
342 | load_this_page_badge();
343 | }
344 |
345 | if ( document.getElementById('coverage_badge') ) {
346 | load_percentages();
347 | }
--------------------------------------------------------------------------------
/source/rst/functions.rst:
--------------------------------------------------------------------------------
1 | .. _functions:
2 |
3 | .. include:: /_static/includes/header.raw
4 |
5 | .. highlight:: python3
6 |
7 |
8 |
9 | *********
10 | Functions
11 | *********
12 |
13 |
14 |
15 | .. index::
16 | single: Python; User-defined functions
17 |
18 | .. contents:: :depth: 2
19 |
20 |
21 |
22 | Overview
23 | ========
24 |
25 | One construct that's extremely useful and provided by almost all programming
26 | languages is **functions**.
27 |
28 | We have already met several functions, such as
29 |
30 | * the ``sqrt()`` function from NumPy and
31 | * the built-in ``print()`` function
32 |
33 | In this lecture we'll treat functions systematically and begin to learn just how
34 | useful and important they are.
35 |
36 | One of the things we will learn to do is build our own user-defined functions
37 |
38 |
39 | We will use the following imports.
40 |
41 | .. code-block:: ipython
42 |
43 | import numpy as np
44 | import matplotlib.pyplot as plt
45 | %matplotlib inline
46 |
47 |
48 |
49 |
50 |
51 | Function Basics
52 | ==================
53 |
54 |
55 | A function is a named section of a program that implements a specific task.
56 |
57 | Many functions exist already and we can use them off the shelf.
58 |
59 | First we review these functions and then discuss how we can build our own.
60 |
61 |
62 | Built-In Functions
63 | ------------------
64 |
65 | Python has a number of *built-in* functions that are available without ``import``.
66 |
67 |
68 | We have already met some
69 |
70 | .. code-block:: python3
71 |
72 | max(19, 20)
73 |
74 | .. code-block:: python3
75 |
76 | print('foobar')
77 |
78 | .. code-block:: python3
79 |
80 | str(22)
81 |
82 | .. code-block:: python3
83 |
84 | type(22)
85 |
86 |
87 | Two more useful built-in functions are ``any()`` and ``all()``
88 |
89 | .. code-block:: python3
90 |
91 | bools = False, True, True
92 | all(bools) # True if all are True and False otherwise
93 |
94 | .. code-block:: python3
95 |
96 | any(bools) # False if all are False and True otherwise
97 |
98 |
99 | The full list of Python built-ins is `here `_.
100 |
101 |
102 | Third Party Functions
103 | ---------------------
104 |
105 | If the built-in functions don't cover what we need, we either need to import
106 | functions or create our own.
107 |
108 | Examples of importing and using functions
109 | were given in the :doc:`previous lecture `
110 |
111 | Here's another one, which tests whether a given year is a leap year:
112 |
113 |
114 | .. code-block:: python3
115 |
116 | import calendar
117 |
118 | calendar.isleap(2020)
119 |
120 |
121 |
122 | Defining Functions
123 | ==================
124 |
125 | In many instances, it is useful to be able to define our own functions.
126 |
127 | This will become clearer as you see more examples.
128 |
129 | Let's start by discussing how it's done.
130 |
131 |
132 | Syntax
133 | ------
134 |
135 | Here's a very simple Python function, that implements the mathematical function
136 | :math:`f(x) = 2 x + 1`
137 |
138 | .. code-block:: python3
139 |
140 | def f(x):
141 | return 2 * x + 1
142 |
143 | Now that we've *defined* this function, let's *call* it and check whether it
144 | does what we expect:
145 |
146 | .. code-block:: python3
147 |
148 | f(1)
149 |
150 | .. code-block:: python3
151 |
152 | f(10)
153 |
154 |
155 |
156 | Here's a longer function, that computes the absolute value of a given number.
157 |
158 | (Such a function already exists as a built-in, but let's write our own for the
159 | exercise.)
160 |
161 | .. code-block:: python3
162 |
163 | def new_abs_function(x):
164 |
165 | if x < 0:
166 | abs_value = -x
167 | else:
168 | abs_value = x
169 |
170 | return abs_value
171 |
172 | Let's review the syntax here.
173 |
174 | * ``def`` is a Python keyword used to start function definitions.
175 |
176 | * ``def new_abs_function(x):`` indicates that the function is called ``new_abs_function`` and that it has a single argument ``x``.
177 |
178 | * The indented code is a code block called the *function body*.
179 |
180 | * The ``return`` keyword indicates that ``abs_value`` is the object that should be returned to the calling code.
181 |
182 | This whole function definition is read by the Python interpreter and stored in memory.
183 |
184 | Let's call it to check that it works:
185 |
186 |
187 | .. code-block:: python3
188 |
189 | print(new_abs_function(3))
190 | print(new_abs_function(-3))
191 |
192 |
193 |
194 | Why Write Functions?
195 | --------------------
196 |
197 | User-defined functions are important for improving the clarity of your code by
198 |
199 | * separating different strands of logic
200 |
201 | * facilitating code reuse
202 |
203 | (Writing the same thing twice is `almost always a bad idea `_)
204 |
205 | We will say more about this :doc:`later `.
206 |
207 |
208 | Applications
209 | ============
210 |
211 |
212 | Random Draws
213 | ------------
214 |
215 |
216 | Consider again this code from the :doc:`previous lecture `
217 |
218 | .. code-block:: python3
219 |
220 | ts_length = 100
221 | ϵ_values = [] # empty list
222 |
223 | for i in range(ts_length):
224 | e = np.random.randn()
225 | ϵ_values.append(e)
226 |
227 | plt.plot(ϵ_values)
228 | plt.show()
229 |
230 |
231 | We will break this program into two parts:
232 |
233 | #. A user-defined function that generates a list of random variables.
234 |
235 | #. The main part of the program that
236 |
237 | #. calls this function to get data
238 |
239 | #. plots the data
240 |
241 | This is accomplished in the next program
242 |
243 | .. _funcloopprog:
244 |
245 | .. code-block:: python3
246 |
247 | def generate_data(n):
248 | ϵ_values = []
249 | for i in range(n):
250 | e = np.random.randn()
251 | ϵ_values.append(e)
252 | return ϵ_values
253 |
254 | data = generate_data(100)
255 | plt.plot(data)
256 | plt.show()
257 |
258 |
259 | When the interpreter gets to the expression ``generate_data(100)``, it executes the function body with ``n`` set equal to 100.
260 |
261 | The net result is that the name ``data`` is *bound* to the list ``ϵ_values`` returned by the function.
262 |
263 |
264 |
265 | Adding Conditions
266 | -----------------
267 |
268 | .. index::
269 | single: Python; Conditions
270 |
271 | Our function ``generate_data()`` is rather limited.
272 |
273 | Let's make it slightly more useful by giving it the ability to return either standard normals or uniform random variables on :math:`(0, 1)` as required.
274 |
275 | This is achieved in the next piece of code.
276 |
277 |
278 | .. _funcloopprog2:
279 |
280 | .. code-block:: python3
281 |
282 |
283 | def generate_data(n, generator_type):
284 | ϵ_values = []
285 | for i in range(n):
286 | if generator_type == 'U':
287 | e = np.random.uniform(0, 1)
288 | else:
289 | e = np.random.randn()
290 | ϵ_values.append(e)
291 | return ϵ_values
292 |
293 | data = generate_data(100, 'U')
294 | plt.plot(data)
295 | plt.show()
296 |
297 | Hopefully, the syntax of the if/else clause is self-explanatory, with indentation again delimiting the extent of the code blocks.
298 |
299 | Notes
300 |
301 | * We are passing the argument ``U`` as a string, which is why we write it as ``'U'``.
302 |
303 | * Notice that equality is tested with the ``==`` syntax, not ``=``.
304 |
305 | * For example, the statement ``a = 10`` assigns the name ``a`` to the value ``10``.
306 |
307 | * The expression ``a == 10`` evaluates to either ``True`` or ``False``, depending on the value of ``a``.
308 |
309 | Now, there are several ways that we can simplify the code above.
310 |
311 | For example, we can get rid of the conditionals all together by just passing the desired generator type *as a function*.
312 |
313 | To understand this, consider the following version.
314 |
315 | .. _test_program_6:
316 |
317 | .. code-block:: python3
318 |
319 |
320 | def generate_data(n, generator_type):
321 | ϵ_values = []
322 | for i in range(n):
323 | e = generator_type()
324 | ϵ_values.append(e)
325 | return ϵ_values
326 |
327 | data = generate_data(100, np.random.uniform)
328 | plt.plot(data)
329 | plt.show()
330 |
331 |
332 | Now, when we call the function ``generate_data()``, we pass ``np.random.uniform``
333 | as the second argument.
334 |
335 | This object is a *function*.
336 |
337 | When the function call ``generate_data(100, np.random.uniform)`` is executed, Python runs the function code block with ``n`` equal to 100 and the name ``generator_type`` "bound" to the function ``np.random.uniform``.
338 |
339 | * While these lines are executed, the names ``generator_type`` and ``np.random.uniform`` are "synonyms", and can be used in identical ways.
340 |
341 | This principle works more generally---for example, consider the following piece of code
342 |
343 | .. code-block:: python3
344 |
345 | max(7, 2, 4) # max() is a built-in Python function
346 |
347 | .. code-block:: python3
348 |
349 | m = max
350 | m(7, 2, 4)
351 |
352 | Here we created another name for the built-in function ``max()``, which could
353 | then be used in identical ways.
354 |
355 | In the context of our program, the ability to bind new names to functions
356 | means that there is no problem *passing a function as an argument to another
357 | function*---as we did above.
358 |
359 |
360 |
361 |
362 |
363 |
364 | Exercises
365 | =========
366 |
367 |
368 |
369 | Exercise 1
370 | ----------
371 |
372 | Recall that :math:`n!` is read as ":math:`n` factorial" and defined as
373 | :math:`n! = n \times (n - 1) \times \cdots \times 2 \times 1`.
374 |
375 | There are functions to compute this in various modules, but let's
376 | write our own version as an exercise.
377 |
378 | In particular, write a function ``factorial`` such that ``factorial(n)`` returns :math:`n!`
379 | for any positive integer :math:`n`.
380 |
381 |
382 |
383 | Exercise 2
384 | ----------
385 |
386 | The `binomial random variable `_ :math:`Y \sim Bin(n, p)` represents the number of successes in :math:`n` binary trials, where each trial succeeds with probability :math:`p`.
387 |
388 | Without any import besides ``from numpy.random import uniform``, write a function
389 | ``binomial_rv`` such that ``binomial_rv(n, p)`` generates one draw of :math:`Y`.
390 |
391 | Hint: If :math:`U` is uniform on :math:`(0, 1)` and :math:`p \in (0,1)`, then the expression ``U < p`` evaluates to ``True`` with probability :math:`p`.
392 |
393 |
394 |
395 |
396 | Exercise 3
397 | ----------
398 |
399 | First, write a function that returns one realization of the following random device
400 |
401 | 1. Flip an unbiased coin 10 times.
402 | 2. If a head occurs ``k`` or more times consecutively within this sequence at least once, pay one dollar.
403 | 3. If not, pay nothing.
404 |
405 | Second, write another function that does the same task except that the second rule of the above random device becomes
406 |
407 | - If a head occurs ``k`` or more times within this sequence, pay one dollar.
408 |
409 | Use no import besides ``from numpy.random import uniform``.
410 |
411 |
412 |
413 |
414 |
415 |
416 | Solutions
417 | =========
418 |
419 |
420 | Exercise 1
421 | ----------
422 |
423 | Here's one solution.
424 |
425 | .. code-block:: python3
426 |
427 | def factorial(n):
428 | k = 1
429 | for i in range(n):
430 | k = k * (i + 1)
431 | return k
432 |
433 | factorial(4)
434 |
435 |
436 |
437 | Exercise 2
438 | ----------
439 |
440 | .. code-block:: python3
441 |
442 | from numpy.random import uniform
443 |
444 | def binomial_rv(n, p):
445 | count = 0
446 | for i in range(n):
447 | U = uniform()
448 | if U < p:
449 | count = count + 1 # Or count += 1
450 | return count
451 |
452 | binomial_rv(10, 0.5)
453 |
454 |
455 |
456 | Exercise 3
457 | ----------
458 |
459 | Here's a function for the first random device.
460 |
461 | .. code-block:: python3
462 |
463 | from numpy.random import uniform
464 |
465 | def draw(k): # pays if k consecutive successes in a sequence
466 |
467 | payoff = 0
468 | count = 0
469 |
470 | for i in range(10):
471 | U = uniform()
472 | count = count + 1 if U < 0.5 else 0
473 | print(count) # print counts for clarity
474 | if count == k:
475 | payoff = 1
476 |
477 | return payoff
478 |
479 | draw(3)
480 |
481 | Here's another function for the second random device.
482 |
483 | .. code-block:: python3
484 |
485 | def draw_new(k): # pays if k successes in a sequence
486 |
487 | payoff = 0
488 | count = 0
489 |
490 | for i in range(10):
491 | U = uniform()
492 | count = count + ( 1 if U < 0.5 else 0 )
493 | print(count)
494 | if count == k:
495 | payoff = 1
496 |
497 | return payoff
498 |
499 | draw_new(3)
500 |
501 |
502 |
--------------------------------------------------------------------------------
/theme/minimal/templates/latex_book.tpl:
--------------------------------------------------------------------------------
1 | ((*- extends 'article.tplx' -*))
2 |
3 | % See http://blog.juliusschulz.de/blog/ultimate-ipython-notebook#templates
4 | % for some useful tips
5 |
6 | %===============================================================================
7 | % Document class
8 | %===============================================================================
9 |
10 | ((* block docclass *))
11 | \documentclass[a4paper,11pt, twoside]{book}
12 | ((* endblock docclass *))
13 |
14 | %===============================================================================
15 | % Packages
16 | %===============================================================================
17 |
18 | ((* block packages *))
19 | \usepackage[T1]{fontenc}
20 | \usepackage{graphicx}
21 | \usepackage[breakable]{tcolorbox}
22 | % We will generate all images so they have a width \maxwidth. This means
23 | % that they will get their normal width if they fit onto the page, but
24 | % are scaled down if they would overflow the margins.
25 | \makeatletter
26 | \def\maxwidth{\ifdim\Gin@nat@width>\linewidth\linewidth
27 | \else\Gin@nat@width\fi}
28 | \makeatother
29 | \let\Oldincludegraphics\includegraphics
30 |
31 | % propose delete%
32 | % Ensure that by default, figures have no caption (until we provide a
33 | % proper Figure object with a Caption API and a way to capture that
34 | % in the conversion process - todo).
35 | % \usepackage{caption}
36 | % \DeclareCaptionLabelFormat{empty}{}
37 | %\captionsetup{format=empty,aboveskip=0pt,belowskip=0pt}
38 | % end - propose delete%
39 |
40 | % float figure settings%
41 | \usepackage{float}
42 | \floatplacement{figure}{H} % used to force figures for placement in text
43 |
44 | \usepackage{adjustbox} % Used to constrain images to a maximum size
45 | \usepackage{xcolor} % Allow colors to be defined
46 | \usepackage{enumerate} % Needed for markdown enumerations to work
47 | \usepackage{geometry} % Used to adjust the document margins
48 | \usepackage{amsmath} % Equations
49 | \usepackage{amssymb} % Equations
50 | \usepackage{textcomp} % defines textquotesingle
51 | % Hack from http://tex.stackexchange.com/a/47451/13684:
52 | \AtBeginDocument{%
53 | \def\PYZsq{\textquotesingle}% Upright quotes in Pygmentized code
54 | }
55 | \usepackage{upquote} % Upright quotes for verbatim code
56 | \usepackage{eurosym} % defines \euro
57 | \usepackage[mathletters]{ucs} % Extended unicode (utf-8) support
58 | \usepackage[utf8x]{inputenc} % Allow utf-8 characters in the tex document
59 | \usepackage{fancyvrb} % verbatim replacement that allows latex
60 | \usepackage{listings}
61 | \lstset{escapeinside={<@}{@>}}
62 | \usepackage{grffile} % extends the file name processing of package graphics
63 | % to support a larger range
64 | % The hyperref package gives us a pdf with properly built
65 | % internal navigation ('pdf bookmarks' for the table of contents,
66 | % internal cross-reference links, web links for URLs, etc.)
67 | \usepackage{hyperref}
68 | \usepackage{longtable} % longtable support required by pandoc >1.10
69 | \usepackage{booktabs} % table support for pandoc > 1.12.2
70 | \usepackage[inline]{enumitem} % IRkernel/repr support (it uses the enumerate* environment)
71 | \usepackage[normalem]{ulem} % ulem is needed to support strikethroughs (\sout)
72 | % normalem makes italics be italics, not underlines
73 | \usepackage{braket}
74 | \usepackage{mathrsfs}
75 | \usepackage{natbib}
76 | \usepackage[document]{ragged2e}
77 | \usepackage{fontspec, unicode-math}
78 | \usepackage[greek,english]{babel}
79 | \usepackage{xunicode}
80 | \usepackage{letltxmacro}
81 | \newcommand{\argmax}{\operatornamewithlimits{argmax}}
82 | \newcommand{\argmin}{\operatornamewithlimits{argmin}}
83 | \DeclareMathOperator{\col}{col}
84 | \setlength{\parskip}{1.5ex plus0.5ex minus0.5ex}
85 | \setlength{\parindent}{0pt}
86 |
87 | \usepackage{letltxmacro}
88 | % https://tex.stackexchange.com/q/88001/5764
89 | \LetLtxMacro\oldttfamily\ttfamily
90 | \DeclareRobustCommand{\ttfamily}{\oldttfamily\csname ttsize\endcsname}
91 | \newcommand{\setttsize}[1]{\def\ttsize{#1}}%
92 |
93 | \DeclareTextFontCommand{\texttt}{\ttfamily}
94 |
95 | % Enable Unicode characters in `Out` code-blocks within verbatim
96 | \usepackage{pmboxdraw}
97 |
98 | % renew commands %
99 | % Set max figure width to be 80% of text width, for now hardcoded.
100 | \renewcommand{\includegraphics}[1]{\begin{center}\Oldincludegraphics[width=.8\maxwidth]{#1}\end{center}}
101 | \renewcommand \caption [2][]{} % removes captions from all figures
102 | \setlist[itemize]{nosep}
103 |
104 | % using CMU Serif for greek and latin letters in code blocks and Liberation Mono for rest%
105 | \setmonofont{Liberation Mono}
106 | \usepackage[Latin,Greek]{ucharclasses}
107 | \newfontfamily\substitutefont{CMU Serif}
108 | \setTransitionsForGreek{\begingroup\substitutefont}{\endgroup}
109 | ((* endblock packages *))
110 |
111 | % Colors for the hyperref package
112 | \definecolor{urlcolor}{rgb}{0,.145,.698}
113 | \definecolor{linkcolor}{rgb}{.71,0.21,0.01}
114 | \definecolor{citecolor}{rgb}{.12,.54,.11}
115 |
116 | % ANSI colors
117 | \definecolor{ansi-black}{HTML}{3E424D}
118 | \definecolor{ansi-black-intense}{HTML}{282C36}
119 | \definecolor{ansi-red}{HTML}{E75C58}
120 | \definecolor{ansi-red-intense}{HTML}{B22B31}
121 | \definecolor{ansi-green}{HTML}{00A250}
122 | \definecolor{ansi-green-intense}{HTML}{007427}
123 | \definecolor{ansi-yellow}{HTML}{DDB62B}
124 | \definecolor{ansi-yellow-intense}{HTML}{B27D12}
125 | \definecolor{ansi-blue}{HTML}{208FFB}
126 | \definecolor{ansi-blue-intense}{HTML}{0065CA}
127 | \definecolor{ansi-magenta}{HTML}{D160C4}
128 | \definecolor{ansi-magenta-intense}{HTML}{A03196}
129 | \definecolor{ansi-cyan}{HTML}{60C6C8}
130 | \definecolor{ansi-cyan-intense}{HTML}{258F8F}
131 | \definecolor{ansi-white}{HTML}{C5C1B4}
132 | \definecolor{ansi-white-intense}{HTML}{A1A6B2}
133 | \definecolor{ansi-default-inverse-fg}{HTML}{FFFFFF}
134 | \definecolor{ansi-default-inverse-bg}{HTML}{000000}
135 |
136 |
137 | % commands and environments needed by pandoc snippets
138 | % extracted from the output of `pandoc -s`
139 | \providecommand{\tightlist}{%
140 | \setlength{\itemsep}{0pt}\setlength{\parskip}{0pt}}
141 | \DefineVerbatimEnvironment{Highlighting}{Verbatim}{commandchars=\\\{\}}
142 | % Add ',fontsize=\small' for more characters per line
143 | \newenvironment{Shaded}{}{}
144 | \newcommand{\KeywordTok}[1]{\textcolor[rgb]{0.00,0.44,0.13}{\textbf{{#1}}}}
145 | \newcommand{\DataTypeTok}[1]{\textcolor[rgb]{0.56,0.13,0.00}{{#1}}}
146 | \newcommand{\DecValTok}[1]{\textcolor[rgb]{0.25,0.63,0.44}{{#1}}}
147 | \newcommand{\BaseNTok}[1]{\textcolor[rgb]{0.25,0.63,0.44}{{#1}}}
148 | \newcommand{\FloatTok}[1]{\textcolor[rgb]{0.25,0.63,0.44}{{#1}}}
149 | \newcommand{\CharTok}[1]{\textcolor[rgb]{0.25,0.44,0.63}{{#1}}}
150 | \newcommand{\StringTok}[1]{\textcolor[rgb]{0.25,0.44,0.63}{{#1}}}
151 | \newcommand{\CommentTok}[1]{\textcolor[rgb]{0.38,0.63,0.69}{\textit{{#1}}}}
152 | \newcommand{\OtherTok}[1]{\textcolor[rgb]{0.00,0.44,0.13}{{#1}}}
153 | \newcommand{\AlertTok}[1]{\textcolor[rgb]{1.00,0.00,0.00}{\textbf{{#1}}}}
154 | \newcommand{\FunctionTok}[1]{\textcolor[rgb]{0.02,0.16,0.49}{{#1}}}
155 | \newcommand{\RegionMarkerTok}[1]{{#1}}
156 | \newcommand{\ErrorTok}[1]{\textcolor[rgb]{1.00,0.00,0.00}{\textbf{{#1}}}}
157 | \newcommand{\NormalTok}[1]{{#1}}
158 |
159 | % Additional commands for more recent versions of Pandoc
160 | \newcommand{\ConstantTok}[1]{\textcolor[rgb]{0.53,0.00,0.00}{{#1}}}
161 | \newcommand{\SpecialCharTok}[1]{\textcolor[rgb]{0.25,0.44,0.63}{{#1}}}
162 | \newcommand{\VerbatimStringTok}[1]{\textcolor[rgb]{0.25,0.44,0.63}{{#1}}}
163 | \newcommand{\SpecialStringTok}[1]{\textcolor[rgb]{0.73,0.40,0.53}{{#1}}}
164 | \newcommand{\ImportTok}[1]{{#1}}
165 | \newcommand{\DocumentationTok}[1]{\textcolor[rgb]{0.73,0.13,0.13}{\textit{{#1}}}}
166 | \newcommand{\AnnotationTok}[1]{\textcolor[rgb]{0.38,0.63,0.69}{\textbf{\textit{{#1}}}}}
167 | \newcommand{\CommentVarTok}[1]{\textcolor[rgb]{0.38,0.63,0.69}{\textbf{\textit{{#1}}}}}
168 | \newcommand{\VariableTok}[1]{\textcolor[rgb]{0.10,0.09,0.49}{{#1}}}
169 | \newcommand{\ControlFlowTok}[1]{\textcolor[rgb]{0.00,0.44,0.13}{\textbf{{#1}}}}
170 | \newcommand{\OperatorTok}[1]{\textcolor[rgb]{0.40,0.40,0.40}{{#1}}}
171 | \newcommand{\BuiltInTok}[1]{{#1}}
172 | \newcommand{\ExtensionTok}[1]{{#1}}
173 | \newcommand{\PreprocessorTok}[1]{\textcolor[rgb]{0.74,0.48,0.00}{{#1}}}
174 | \newcommand{\AttributeTok}[1]{\textcolor[rgb]{0.49,0.56,0.16}{{#1}}}
175 | \newcommand{\InformationTok}[1]{\textcolor[rgb]{0.38,0.63,0.69}{\textbf{\textit{{#1}}}}}
176 | \newcommand{\WarningTok}[1]{\textcolor[rgb]{0.38,0.63,0.69}{\textbf{\textit{{#1}}}}}
177 |
178 |
179 | % Define a nice break command that doesn't care if a line doesn't already
180 | % exist.
181 | \def\br{\hspace*{\fill} \\* }
182 | % Math Jax compatibility definitions
183 | \def\gt{>}
184 | \def\lt{<}
185 | \let\Oldtex\TeX
186 | \let\Oldlatex\LaTeX
187 | \renewcommand{\TeX}{\textrm{\Oldtex}}
188 | \renewcommand{\LaTeX}{\textrm{\Oldlatex}
189 |
190 | %===============================================================================
191 | % Title Page
192 | %===============================================================================
193 |
194 | ((* block maketitle *))
195 | \setttsize{\footnotesize}
196 |
197 | ((*- if nb.metadata.get("latex_metadata", {}).get("jupyter_pdf_book_title", ""): -*))
198 | \title{\Huge \textbf{((( nb.metadata["latex_metadata"]["jupyter_pdf_book_title"] )))}}
199 | ((*- endif *))
200 |
201 | ((*- if nb.metadata.get("latex_metadata", {}).get("author", ""): -*))
202 | \author{\textsc{((( nb.metadata["latex_metadata"]["author"] )))}}
203 | ((*- endif *))
204 |
205 | ((*- if nb.metadata.get("latex_metadata", {}).get("affiliation", ""): -*))
206 | \affiliation{((( nb.metadata["latex_metadata"]["affiliation"] )))}
207 | ((*- endif *))
208 |
209 | \date{\today}
210 | \maketitle
211 |
212 | \setcounter{tocdepth}{0}
213 | \tableofcontents
214 |
215 |
216 | \parskip 0.09in
217 |
218 | \mainmatter
219 |
220 | ((* endblock maketitle *))
221 |
222 | %===============================================================================
223 | % Input
224 | %===============================================================================
225 |
226 | % Input cells can be hidden using the "Hide input" and "Hide input all"
227 | % nbextensions (which set the hide_input metadata flags)
228 |
229 | ((* block input scoped *))
230 | ((( cell.metadata.get("hide_input", "") )))
231 | ((*- if cell.metadata.hide_input or nb.metadata.hide_input or cell.metadata.get("hide_input", ""): -*))
232 | ((*- else -*))
233 | ((( custom_add_prompt(cell.source | wrap_text(88) | highlight_code(strip_verbatim=True), cell, 'In ', 'incolor', 'plain') )))
234 | ((*- endif *))
235 | ((* endblock input *))
236 |
237 |
238 | %===============================================================================
239 | % Output
240 | %===============================================================================
241 |
242 | ((* block output_group -*))
243 | ((*- if cell.metadata.hide_output: -*))
244 | ((*- else -*))
245 | ((( super() )))
246 | ((*- endif -*))
247 | ((* endblock output_group *))
248 |
249 | ((* block execute_result scoped *))
250 | ((*- for type in output.data | filter_data_type -*))
251 | ((*- if type in ['text/plain']*))
252 | ((( custom_add_prompt(output.data['text/plain'] | wrap_text(88) | escape_latex | ansi2latex, cell, 'Out', 'outcolor', 'plain') )))
253 | ((*- elif type in ['text/latex']*))
254 | ((( custom_add_prompt(output.data['text/latex'] | wrap_text(88) | ansi2latex, cell, 'Out', 'outcolor', 'latex') )))
255 | ((* else -*))
256 | ((( custom_add_prompt( '' | wrap_text(88)| escape_latex | ansi2latex, cell, 'Out', 'outcolor', 'plain') )))
257 | ((*- endif -*))
258 | ((*- endfor -*))
259 | ((* endblock execute_result *))
260 |
261 | % Display stream ouput with coloring
262 | ((* block stream *))
263 | \begin{Verbatim}[commandchars=\\\{\}, fontsize=\footnotesize]
264 | ((( output.text | wrap_text(86) | escape_latex | ansi2latex )))
265 | \end{Verbatim}
266 | %((* endblock stream *))
267 |
268 | %==============================================================================
269 | % Define macro custom_add_prompt() (derived from add_prompt() macro in style_ipython.tplx)
270 | %==============================================================================
271 |
272 | ((* macro custom_add_prompt(text, cell, prompt, prompt_color, type) -*))
273 | ((*- if cell.execution_count is defined -*))
274 | ((*- set execution_count = "" ~ (cell.execution_count | replace(None, " ")) -*))
275 | ((*- else -*))
276 | ((*- set execution_count = "" -*))
277 | ((*- endif -*))
278 | ((*- set indention = " " * (execution_count | length + 7) -*))
279 | ((*- if type == 'plain' -*))
280 | \begin{Verbatim}[commandchars=\\\{\}, fontsize=\small, xleftmargin=-3.9em]
281 | ((( text.replace('$$','').replace('$\\',"\\(\\").replace('$','\)') | add_prompts(first='{\color{' ~ prompt_color ~ '}' ~ prompt ~ '[{\\color{' ~ prompt_color ~ '}' ~ execution_count ~ '}]:} ', cont=indention) )))
282 | \end{Verbatim}
283 | ((*- else -*))
284 | \begin{lstlisting}[mathescape, basicstyle=\small\ttfamily\color{black}, xleftmargin=-3.9em]
285 | ((( text | add_prompts(first='<@\\textcolor{red}{' ~ prompt ~ '[' ~ execution_count ~ ']: }@>', cont=indention) )))
286 | \end{lstlisting}
287 | ((*- endif -*))
288 | ((*- endmacro *))
289 |
290 | %==============================================================================
291 | % Support Macros
292 | %==============================================================================
293 |
294 | % Name: draw_prompt
295 | % Purpose: Renders an output/input prompt
296 | ((* macro draw_prompt(cell, prompt, prompt_color, extra_space) -*))
297 | ((*- if cell.execution_count is defined -*))
298 | ((*- set execution_count = "" ~ (cell.execution_count | replace(None, " ")) -*))
299 | ((*- else -*))((*- set execution_count = " " -*))((*- endif *))
300 |
301 | ((*- if (resources.global_content_filter.include_output_prompt and prompt == 'Out')
302 | or (resources.global_content_filter.include_input_prompt and prompt == 'In' ) *))
303 | \prompt{(((prompt)))}{(((prompt_color)))}{(((execution_count)))}{(((extra_space)))}
304 | ((*- endif -*))
305 | ((*- endmacro *))
306 |
307 |
308 | %==============================================================================
309 | % Bibliography
310 | %==============================================================================
311 |
312 | % Insert citations in markdown as e.g.
313 | % [DevoretS2013]
314 | % requires file references.bib in current directory (or the file set as "bib" in the latex_metadata)
315 |
316 |
317 | ((* block bibliography *))
318 | % Add a bibliography block to the postdoc
319 | ((* endblock bibliography *))
320 |
--------------------------------------------------------------------------------
/theme/minimal/templates/latex.tpl:
--------------------------------------------------------------------------------
1 | ((*- extends 'article.tplx' -*))
2 |
3 | % See http://blog.juliusschulz.de/blog/ultimate-ipython-notebook#templates
4 | % for some useful tips
5 |
6 | %===============================================================================
7 | % Document class
8 | %===============================================================================
9 |
10 | ((* block docclass *))
11 | \documentclass[11pt, twoside, a4paper]{article}
12 | ((* endblock docclass *))
13 |
14 | %===============================================================================
15 | % Packages
16 | %===============================================================================
17 |
18 | ((* block packages *))
19 | \usepackage[T1]{fontenc}
20 | \usepackage{graphicx}
21 | \usepackage[breakable]{tcolorbox}
22 | % We will generate all images so they have a width \maxwidth. This means
23 | % that they will get their normal width if they fit onto the page, but
24 | % are scaled down if they would overflow the margins.
25 | \makeatletter
26 | \def\maxwidth{\ifdim\Gin@nat@width>\linewidth\linewidth
27 | \else\Gin@nat@width\fi}
28 | \makeatother
29 | \let\Oldincludegraphics\includegraphics
30 |
31 | % propose delete%
32 | % Ensure that by default, figures have no caption (until we provide a
33 | % proper Figure object with a Caption API and a way to capture that
34 | % in the conversion process - todo).
35 | % \usepackage{caption}
36 | % \DeclareCaptionLabelFormat{empty}{}
37 | %\captionsetup{format=empty,aboveskip=0pt,belowskip=0pt}
38 | % end - propose delete%
39 |
40 | % float figure settings%
41 | \usepackage{float}
42 | \floatplacement{figure}{H} % used to force figures for placement in text
43 |
44 | \usepackage{adjustbox} % Used to constrain images to a maximum size
45 | \usepackage{xcolor} % Allow colors to be defined
46 | \usepackage{enumerate} % Needed for markdown enumerations to work
47 | \usepackage{geometry} % Used to adjust the document margins
48 | \usepackage{amsmath} % Equations
49 | \usepackage{amssymb} % Equations
50 | \usepackage{textcomp} % defines textquotesingle
51 | % Hack from http://tex.stackexchange.com/a/47451/13684:
52 | \AtBeginDocument{%
53 | \def\PYZsq{\textquotesingle}% Upright quotes in Pygmentized code
54 | }
55 | \usepackage{upquote} % Upright quotes for verbatim code
56 | \usepackage{eurosym} % defines \euro
57 | \usepackage[mathletters]{ucs} % Extended unicode (utf-8) support
58 | \usepackage[utf8x]{inputenc} % Allow utf-8 characters in the tex document
59 | \usepackage{fancyvrb} % verbatim replacement that allows latex
60 | \usepackage{xcolor}
61 | \usepackage{listings}
62 | \lstset{escapeinside={<@}{@>}}
63 | \usepackage{grffile} % extends the file name processing of package graphics
64 | % to support a larger range
65 | % The hyperref package gives us a pdf with properly built
66 | % internal navigation ('pdf bookmarks' for the table of contents,
67 | % internal cross-reference links, web links for URLs, etc.)
68 | \usepackage{hyperref}
69 | \usepackage{longtable} % longtable support required by pandoc >1.10
70 | \usepackage{booktabs} % table support for pandoc > 1.12.2
71 | \usepackage[inline]{enumitem} % IRkernel/repr support (it uses the enumerate* environment)
72 | \usepackage[normalem]{ulem} % ulem is needed to support strikethroughs (\sout)
73 | % normalem makes italics be italics, not underlines
74 | \usepackage{braket}
75 | \usepackage{mathrsfs}
76 | \usepackage{natbib}
77 | \usepackage[document]{ragged2e}
78 | \usepackage{fontspec, unicode-math}
79 | \usepackage[greek,english]{babel}
80 | \usepackage{xunicode}
81 | \usepackage{letltxmacro}
82 | \newcommand{\argmax}{\operatornamewithlimits{argmax}}
83 | \newcommand{\argmin}{\operatornamewithlimits{argmin}}
84 | \DeclareMathOperator{\col}{col}
85 | \setlength{\parskip}{1.5ex plus0.5ex minus0.5ex}
86 | \setlength{\parindent}{0pt}
87 |
88 | \usepackage{letltxmacro}
89 | % https://tex.stackexchange.com/q/88001/5764
90 | \LetLtxMacro\oldttfamily\ttfamily
91 | \DeclareRobustCommand{\ttfamily}{\oldttfamily\csname ttsize\endcsname}
92 | \newcommand{\setttsize}[1]{\def\ttsize{#1}}%
93 |
94 | \DeclareTextFontCommand{\texttt}{\ttfamily}
95 |
96 | % Enable Unicode characters in `Out` code-blocks within verbatim
97 | \usepackage{pmboxdraw}
98 |
99 | % renew commands %
100 | % Set max figure width to be 80% of text width, for now hardcoded.
101 | \renewcommand{\includegraphics}[1]{\begin{center}\Oldincludegraphics[width=.8\maxwidth]{#1}\end{center}}
102 | \renewcommand \caption [2][]{} % removes captions from all figures
103 | \setlist[itemize]{nosep}
104 |
105 | % using CMU Serif for greek and latin letters in code blocks and Liberation Mono for rest%
106 | \setmonofont{Liberation Mono}
107 | \usepackage[Latin,Greek]{ucharclasses}
108 | \newfontfamily\substitutefont{CMU Serif}
109 | \setTransitionsForGreek{\begingroup\substitutefont}{\endgroup}
110 | ((* endblock packages *))
111 |
112 | % Colors for the hyperref package
113 | \definecolor{urlcolor}{rgb}{0,.145,.698}
114 | \definecolor{linkcolor}{rgb}{.71,0.21,0.01}
115 | \definecolor{citecolor}{rgb}{.12,.54,.11}
116 |
117 | % ANSI colors
118 | \definecolor{ansi-black}{HTML}{3E424D}
119 | \definecolor{ansi-black-intense}{HTML}{282C36}
120 | \definecolor{ansi-red}{HTML}{E75C58}
121 | \definecolor{ansi-red-intense}{HTML}{B22B31}
122 | \definecolor{ansi-green}{HTML}{00A250}
123 | \definecolor{ansi-green-intense}{HTML}{007427}
124 | \definecolor{ansi-yellow}{HTML}{DDB62B}
125 | \definecolor{ansi-yellow-intense}{HTML}{B27D12}
126 | \definecolor{ansi-blue}{HTML}{208FFB}
127 | \definecolor{ansi-blue-intense}{HTML}{0065CA}
128 | \definecolor{ansi-magenta}{HTML}{D160C4}
129 | \definecolor{ansi-magenta-intense}{HTML}{A03196}
130 | \definecolor{ansi-cyan}{HTML}{60C6C8}
131 | \definecolor{ansi-cyan-intense}{HTML}{258F8F}
132 | \definecolor{ansi-white}{HTML}{C5C1B4}
133 | \definecolor{ansi-white-intense}{HTML}{A1A6B2}
134 | \definecolor{ansi-default-inverse-fg}{HTML}{FFFFFF}
135 | \definecolor{ansi-default-inverse-bg}{HTML}{000000}
136 |
137 |
138 | % commands and environments needed by pandoc snippets
139 | % extracted from the output of `pandoc -s`
140 | \providecommand{\tightlist}{%
141 | \setlength{\itemsep}{0pt}\setlength{\parskip}{0pt}}
142 | \DefineVerbatimEnvironment{Highlighting}{Verbatim}{commandchars=\\\{\}}
143 | % Add ',fontsize=\small' for more characters per line
144 | \newenvironment{Shaded}{}{}
145 | \newcommand{\KeywordTok}[1]{\textcolor[rgb]{0.00,0.44,0.13}{\textbf{{#1}}}}
146 | \newcommand{\DataTypeTok}[1]{\textcolor[rgb]{0.56,0.13,0.00}{{#1}}}
147 | \newcommand{\DecValTok}[1]{\textcolor[rgb]{0.25,0.63,0.44}{{#1}}}
148 | \newcommand{\BaseNTok}[1]{\textcolor[rgb]{0.25,0.63,0.44}{{#1}}}
149 | \newcommand{\FloatTok}[1]{\textcolor[rgb]{0.25,0.63,0.44}{{#1}}}
150 | \newcommand{\CharTok}[1]{\textcolor[rgb]{0.25,0.44,0.63}{{#1}}}
151 | \newcommand{\StringTok}[1]{\textcolor[rgb]{0.25,0.44,0.63}{{#1}}}
152 | \newcommand{\CommentTok}[1]{\textcolor[rgb]{0.38,0.63,0.69}{\textit{{#1}}}}
153 | \newcommand{\OtherTok}[1]{\textcolor[rgb]{0.00,0.44,0.13}{{#1}}}
154 | \newcommand{\AlertTok}[1]{\textcolor[rgb]{1.00,0.00,0.00}{\textbf{{#1}}}}
155 | \newcommand{\FunctionTok}[1]{\textcolor[rgb]{0.02,0.16,0.49}{{#1}}}
156 | \newcommand{\RegionMarkerTok}[1]{{#1}}
157 | \newcommand{\ErrorTok}[1]{\textcolor[rgb]{1.00,0.00,0.00}{\textbf{{#1}}}}
158 | \newcommand{\NormalTok}[1]{{#1}}
159 |
160 | % Additional commands for more recent versions of Pandoc
161 | \newcommand{\ConstantTok}[1]{\textcolor[rgb]{0.53,0.00,0.00}{{#1}}}
162 | \newcommand{\SpecialCharTok}[1]{\textcolor[rgb]{0.25,0.44,0.63}{{#1}}}
163 | \newcommand{\VerbatimStringTok}[1]{\textcolor[rgb]{0.25,0.44,0.63}{{#1}}}
164 | \newcommand{\SpecialStringTok}[1]{\textcolor[rgb]{0.73,0.40,0.53}{{#1}}}
165 | \newcommand{\ImportTok}[1]{{#1}}
166 | \newcommand{\DocumentationTok}[1]{\textcolor[rgb]{0.73,0.13,0.13}{\textit{{#1}}}}
167 | \newcommand{\AnnotationTok}[1]{\textcolor[rgb]{0.38,0.63,0.69}{\textbf{\textit{{#1}}}}}
168 | \newcommand{\CommentVarTok}[1]{\textcolor[rgb]{0.38,0.63,0.69}{\textbf{\textit{{#1}}}}}
169 | \newcommand{\VariableTok}[1]{\textcolor[rgb]{0.10,0.09,0.49}{{#1}}}
170 | \newcommand{\ControlFlowTok}[1]{\textcolor[rgb]{0.00,0.44,0.13}{\textbf{{#1}}}}
171 | \newcommand{\OperatorTok}[1]{\textcolor[rgb]{0.40,0.40,0.40}{{#1}}}
172 | \newcommand{\BuiltInTok}[1]{{#1}}
173 | \newcommand{\ExtensionTok}[1]{{#1}}
174 | \newcommand{\PreprocessorTok}[1]{\textcolor[rgb]{0.74,0.48,0.00}{{#1}}}
175 | \newcommand{\AttributeTok}[1]{\textcolor[rgb]{0.49,0.56,0.16}{{#1}}}
176 | \newcommand{\InformationTok}[1]{\textcolor[rgb]{0.38,0.63,0.69}{\textbf{\textit{{#1}}}}}
177 | \newcommand{\WarningTok}[1]{\textcolor[rgb]{0.38,0.63,0.69}{\textbf{\textit{{#1}}}}}
178 |
179 |
180 | % Define a nice break command that doesn't care if a line doesn't already
181 | % exist.
182 | \def\br{\hspace*{\fill} \\* }
183 | % Math Jax compatibility definitions
184 | \def\gt{>}
185 | \def\lt{<}
186 | \let\Oldtex\TeX
187 | \let\Oldlatex\LaTeX
188 | \renewcommand{\TeX}{\textrm{\Oldtex}}
189 | \renewcommand{\LaTeX}{\textrm{\Oldlatex}
190 |
191 | %===============================================================================
192 | % Title Page
193 | %===============================================================================
194 |
195 | ((* block maketitle *))
196 | \setttsize{\footnotesize}
197 |
198 | \title{((( nb.metadata.get("latex_metadata", {}).get("title", "") | escape_latex )))}
199 |
200 | ((*- if nb.metadata.get("latex_metadata", {}).get("author", ""): -*))
201 | \author{((( nb.metadata["latex_metadata"]["author"] )))}
202 | ((*- endif *))
203 |
204 | ((*- if nb.metadata.get("latex_metadata", {}).get("affiliation", ""): -*))
205 | \affiliation{((( nb.metadata["latex_metadata"]["affiliation"] )))}
206 | ((*- endif *))
207 |
208 | \date{\today}
209 | \maketitle
210 |
211 | ((*- if nb.metadata.get("latex_metadata", {}).get("logo", ""): -*))
212 | \begin{center}
213 | \adjustimage{max size={0.6\linewidth}{0.6\paperheight}}{((( nb.metadata["latex_metadata"]["logo"] )))}
214 | \end{center}
215 | ((*- endif -*))
216 |
217 | % delete-till-here-book %
218 | ((* endblock maketitle *))
219 |
220 |
221 | %===============================================================================
222 | % Input
223 | %===============================================================================
224 |
225 | % Input cells can be hidden using the "Hide input" and "Hide input all"
226 | % nbextensions (which set the hide_input metadata flags)
227 |
228 | ((* block input scoped *))
229 | ((( cell.metadata.get("hide_input", "") )))
230 | ((*- if cell.metadata.hide_input or nb.metadata.hide_input or cell.metadata.get("hide_input", ""): -*))
231 | ((*- else -*))
232 | ((( custom_add_prompt(cell.source | wrap_text(88) | highlight_code(strip_verbatim=True), cell, 'In ', 'incolor', 'plain') )))
233 | ((*- endif *))
234 | ((* endblock input *))
235 |
236 |
237 | %===============================================================================
238 | % Output
239 | %===============================================================================
240 |
241 | ((* block output_group -*))
242 | ((*- if cell.metadata.hide_output: -*))
243 | ((*- else -*))
244 | ((( super() )))
245 | ((*- endif -*))
246 | ((* endblock output_group *))
247 |
248 | ((* block execute_result scoped *))
249 | ((*- for type in output.data | filter_data_type -*))
250 | ((*- if type in ['text/plain']*))
251 | ((( custom_add_prompt(output.data['text/plain'] | wrap_text(88) | escape_latex | ansi2latex, cell, 'Out', 'outcolor', 'plain') )))
252 | ((*- elif type in ['text/latex']*))
253 | ((( custom_add_prompt(output.data['text/latex'] | wrap_text(88) | ansi2latex, cell, 'Out', 'outcolor', 'latex') )))
254 | ((* else -*))
255 | ((( custom_add_prompt( '' | wrap_text(88)| escape_latex | ansi2latex, cell, 'Out', 'outcolor', 'plain') )))
256 | ((*- endif -*))
257 | ((*- endfor -*))
258 | ((* endblock execute_result *))
259 |
260 | % Display stream ouput with coloring
261 | ((* block stream *))
262 | \begin{Verbatim}[commandchars=\\\{\}, fontsize=\footnotesize]
263 | ((( output.text | wrap_text(86) | escape_latex | ansi2latex )))
264 | \end{Verbatim}
265 | %((* endblock stream *))
266 |
267 | %==============================================================================
268 | % Define macro custom_add_prompt() (derived from add_prompt() macro in style_ipython.tplx)
269 | %==============================================================================
270 |
271 | ((* macro custom_add_prompt(text, cell, prompt, prompt_color, type) -*))
272 | ((*- if cell.execution_count is defined -*))
273 | ((*- set execution_count = "" ~ (cell.execution_count | replace(None, " ")) -*))
274 | ((*- else -*))
275 | ((*- set execution_count = "" -*))
276 | ((*- endif -*))
277 | ((*- set indention = " " * (execution_count | length + 7) -*))
278 | ((*- if type == 'plain' -*))
279 | \begin{Verbatim}[commandchars=\\\{\}, fontsize=\small, xleftmargin=-3.9em]
280 | ((( text.replace('$$','').replace('$\\',"\\(\\").replace('$','\)') | add_prompts(first='{\color{' ~ prompt_color ~ '}' ~ prompt ~ '[{\\color{' ~ prompt_color ~ '}' ~ execution_count ~ '}]:} ', cont=indention) )))
281 | \end{Verbatim}
282 | ((*- else -*))
283 | \begin{lstlisting}[mathescape, basicstyle=\small\ttfamily\color{black}, keywordstyle={\color{red}}, xleftmargin=-4.8em]
284 | ((( text | add_prompts(first='<@\\textcolor{red}{' ~ prompt ~ '[' ~ execution_count ~ ']: }@>', cont=indention) )))
285 | \end{lstlisting}
286 | ((*- endif -*))
287 | ((*- endmacro *))
288 |
289 | %==============================================================================
290 | % Support Macros
291 | %==============================================================================
292 |
293 | % Name: draw_prompt
294 | % Purpose: Renders an output/input prompt
295 | ((* macro draw_prompt(cell, prompt, prompt_color, extra_space) -*))
296 | ((*- if cell.execution_count is defined -*))
297 | ((*- set execution_count = "" ~ (cell.execution_count | replace(None, " ")) -*))
298 | ((*- else -*))((*- set execution_count = " " -*))((*- endif *))
299 |
300 | ((*- if (resources.global_content_filter.include_output_prompt and prompt == 'Out')
301 | or (resources.global_content_filter.include_input_prompt and prompt == 'In' ) *))
302 | \prompt{(((prompt)))}{(((prompt_color)))}{(((execution_count)))}{(((extra_space)))}
303 | ((*- endif -*))
304 | ((*- endmacro *))
305 |
306 |
307 | %==============================================================================
308 | % Bibliography
309 | %==============================================================================
310 |
311 | % Insert citations in markdown as e.g.
312 | % [DevoretS2013]
313 | % requires file references.bib in current directory (or the file set as "bib" in the latex_metadata)
314 |
315 |
316 | ((* block bibliography *))
317 | % delete-from-here-book %
318 | ((*- if nb.metadata.get("latex_metadata", {}).get("bib_include", ""): -*))
319 | % Add a bibliography block to the postdoc
320 | \bibliographystyle{plain}
321 | \bibliography{((( nb.metadata.get("latex_metadata", {}).get("bib", "quant-econ") )))}
322 | ((*- endif -*))
323 | ((* endblock bibliography *))
324 |
--------------------------------------------------------------------------------
/source/rst/writing_good_code.rst:
--------------------------------------------------------------------------------
1 | .. _writing_good_code:
2 |
3 | .. include:: /_static/includes/header.raw
4 |
5 | .. highlight:: python3
6 |
7 | *****************
8 | Writing Good Code
9 | *****************
10 |
11 | .. index::
12 | single: Models; Code style
13 |
14 | .. contents:: :depth: 2
15 |
16 |
17 |
18 | Overview
19 | ========
20 |
21 | When computer programs are small, poorly written code is not overly costly.
22 |
23 | But more data, more sophisticated models, and more computer power are enabling us to take on more challenging problems that involve writing longer programs.
24 |
25 | For such programs, investment in good coding practices will pay high returns.
26 |
27 | The main payoffs are higher productivity and faster code.
28 |
29 | In this lecture, we review some elements of good coding practice.
30 |
31 | We also touch on modern developments in scientific computing --- such as just in time compilation --- and how they affect good program design.
32 |
33 |
34 |
35 |
36 | An Example of Poor Code
37 | =======================
38 |
39 | Let's have a look at some poorly written code.
40 |
41 | The job of the code is to generate and plot time series of the simplified Solow model
42 |
43 | .. math::
44 | :label: gc_solmod
45 |
46 | k_{t+1} = s k_t^{\alpha} + (1 - \delta) k_t,
47 | \quad t = 0, 1, 2, \ldots
48 |
49 |
50 | Here
51 |
52 | * :math:`k_t` is capital at time :math:`t` and
53 |
54 | * :math:`s, \alpha, \delta` are parameters (savings, a productivity parameter and depreciation)
55 |
56 | For each parameterization, the code
57 |
58 | #. sets :math:`k_0 = 1`
59 |
60 | #. iterates using :eq:`gc_solmod` to produce a sequence :math:`k_0, k_1, k_2 \ldots , k_T`
61 |
62 | #. plots the sequence
63 |
64 | The plots will be grouped into three subfigures.
65 |
66 | In each subfigure, two parameters are held fixed while another varies
67 |
68 | .. code-block:: ipython
69 |
70 | import numpy as np
71 | import matplotlib.pyplot as plt
72 | %matplotlib inline
73 |
74 | # Allocate memory for time series
75 | k = np.empty(50)
76 |
77 | fig, axes = plt.subplots(3, 1, figsize=(6, 14))
78 |
79 | # Trajectories with different α
80 | δ = 0.1
81 | s = 0.4
82 | α = (0.25, 0.33, 0.45)
83 |
84 | for j in range(3):
85 | k[0] = 1
86 | for t in range(49):
87 | k[t+1] = s * k[t]**α[j] + (1 - δ) * k[t]
88 | axes[0].plot(k, 'o-', label=rf"$\alpha = {α[j]},\; s = {s},\; \delta={δ}$")
89 |
90 | axes[0].grid(lw=0.2)
91 | axes[0].set_ylim(0, 18)
92 | axes[0].set_xlabel('time')
93 | axes[0].set_ylabel('capital')
94 | axes[0].legend(loc='upper left', frameon=True)
95 |
96 | # Trajectories with different s
97 | δ = 0.1
98 | α = 0.33
99 | s = (0.3, 0.4, 0.5)
100 |
101 | for j in range(3):
102 | k[0] = 1
103 | for t in range(49):
104 | k[t+1] = s[j] * k[t]**α + (1 - δ) * k[t]
105 | axes[1].plot(k, 'o-', label=rf"$\alpha = {α},\; s = {s[j]},\; \delta={δ}$")
106 |
107 | axes[1].grid(lw=0.2)
108 | axes[1].set_xlabel('time')
109 | axes[1].set_ylabel('capital')
110 | axes[1].set_ylim(0, 18)
111 | axes[1].legend(loc='upper left', frameon=True)
112 |
113 | # Trajectories with different δ
114 | δ = (0.05, 0.1, 0.15)
115 | α = 0.33
116 | s = 0.4
117 |
118 | for j in range(3):
119 | k[0] = 1
120 | for t in range(49):
121 | k[t+1] = s * k[t]**α + (1 - δ[j]) * k[t]
122 | axes[2].plot(k, 'o-', label=rf"$\alpha = {α},\; s = {s},\; \delta={δ[j]}$")
123 |
124 | axes[2].set_ylim(0, 18)
125 | axes[2].set_xlabel('time')
126 | axes[2].set_ylabel('capital')
127 | axes[2].grid(lw=0.2)
128 | axes[2].legend(loc='upper left', frameon=True)
129 |
130 | plt.show()
131 |
132 |
133 | True, the code more or less follows `PEP8 `__.
134 |
135 | At the same time, it's very poorly structured.
136 |
137 | Let's talk about why that's the case, and what we can do about it.
138 |
139 |
140 | Good Coding Practice
141 | ====================
142 |
143 | There are usually many different ways to write a program that accomplishes a given task.
144 |
145 | For small programs, like the one above, the way you write code doesn't matter too much.
146 |
147 | But if you are ambitious and want to produce useful things, you'll write medium to large programs too.
148 |
149 | In those settings, coding style matters **a great deal**.
150 |
151 | Fortunately, lots of smart people have thought about the best way to write code.
152 |
153 | Here are some basic precepts.
154 |
155 |
156 |
157 | Don't Use Magic Numbers
158 | -----------------------
159 |
160 | If you look at the code above, you'll see numbers like ``50`` and ``49`` and ``3`` scattered through the code.
161 |
162 | These kinds of numeric literals in the body of your code are sometimes called "magic numbers".
163 |
164 | This is not a compliment.
165 |
166 | While numeric literals are not all evil, the numbers shown in the program above
167 | should certainly be replaced by named constants.
168 |
169 | For example, the code above could declare the variable ``time_series_length = 50``.
170 |
171 | Then in the loops, ``49`` should be replaced by ``time_series_length - 1``.
172 |
173 | The advantages are:
174 |
175 | * the meaning is much clearer throughout
176 |
177 | * to alter the time series length, you only need to change one value
178 |
179 |
180 | Don't Repeat Yourself
181 | ---------------------
182 |
183 | The other mortal sin in the code snippet above is repetition.
184 |
185 | Blocks of logic (such as the loop to generate time series) are repeated with only minor changes.
186 |
187 | This violates a fundamental tenet of programming: Don't repeat yourself (DRY).
188 |
189 | * Also called DIE (duplication is evil).
190 |
191 | Yes, we realize that you can just cut and paste and change a few symbols.
192 |
193 | But as a programmer, your aim should be to **automate** repetition, **not** do it yourself.
194 |
195 | More importantly, repeating the same logic in different places means that eventually one of them will likely be wrong.
196 |
197 | If you want to know more, read the excellent summary found on `this page `__.
198 |
199 | We'll talk about how to avoid repetition below.
200 |
201 |
202 | Minimize Global Variables
203 | -------------------------
204 |
205 | Sure, global variables (i.e., names assigned to values outside of any function or class) are convenient.
206 |
207 | Rookie programmers typically use global variables with abandon --- as we once did ourselves.
208 |
209 | But global variables are dangerous, especially in medium to large size programs, since
210 |
211 | * they can affect what happens in any part of your program
212 |
213 | * they can be changed by any function
214 |
215 | This makes it much harder to be certain about what some small part of a given piece of code actually commands.
216 |
217 | Here's a `useful discussion on the topic `__.
218 |
219 | While the odd global in small scripts is no big deal, we recommend that you teach yourself to avoid them.
220 |
221 | (We'll discuss how just below).
222 |
223 |
224 | JIT Compilation
225 | ^^^^^^^^^^^^^^^
226 |
227 | For scientific computing, there is another good reason to avoid global variables.
228 |
229 | As :doc:`we've seen in previous lectures `, JIT compilation can generate excellent performance for scripting languages like Python.
230 |
231 | But the task of the compiler used for JIT compilation becomes harder when global variables are present.
232 |
233 | Put differently, the type inference required for JIT compilation is safer and
234 | more effective when variables are sandboxed inside a function.
235 |
236 |
237 | Use Functions or Classes
238 | ------------------------
239 |
240 | Fortunately, we can easily avoid the evils of global variables and WET code.
241 |
242 | * WET stands for "we enjoy typing" and is the opposite of DRY.
243 |
244 | We can do this by making frequent use of functions or classes.
245 |
246 | In fact, functions and classes are designed specifically to help us avoid shaming ourselves by repeating code or excessive use of global variables.
247 |
248 |
249 | Which One, Functions or Classes?
250 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
251 |
252 | Both can be useful, and in fact they work well with each other.
253 |
254 | We'll learn more about these topics over time.
255 |
256 | (Personal preference is part of the story too)
257 |
258 | What's really important is that you use one or the other or both.
259 |
260 |
261 |
262 | Revisiting the Example
263 | ======================
264 |
265 | Here's some code that reproduces the plot above with better coding style.
266 |
267 |
268 | .. code-block:: python3
269 |
270 | from itertools import product
271 |
272 | def plot_path(ax, αs, s_vals, δs, time_series_length=50):
273 | """
274 | Add a time series plot to the axes ax for all given parameters.
275 | """
276 | k = np.empty(time_series_length)
277 |
278 | for (α, s, δ) in product(αs, s_vals, δs):
279 | k[0] = 1
280 | for t in range(time_series_length-1):
281 | k[t+1] = s * k[t]**α + (1 - δ) * k[t]
282 | ax.plot(k, 'o-', label=rf"$\alpha = {α},\; s = {s},\; \delta = {δ}$")
283 |
284 | ax.set_xlabel('time')
285 | ax.set_ylabel('capital')
286 | ax.set_ylim(0, 18)
287 | ax.legend(loc='upper left', frameon=True)
288 |
289 | fig, axes = plt.subplots(3, 1, figsize=(6, 14))
290 |
291 | # Parameters (αs, s_vals, δs)
292 | set_one = ([0.25, 0.33, 0.45], [0.4], [0.1])
293 | set_two = ([0.33], [0.3, 0.4, 0.5], [0.1])
294 | set_three = ([0.33], [0.4], [0.05, 0.1, 0.15])
295 |
296 | for (ax, params) in zip(axes, (set_one, set_two, set_three)):
297 | αs, s_vals, δs = params
298 | plot_path(ax, αs, s_vals, δs)
299 |
300 | plt.show()
301 |
302 |
303 | If you inspect this code, you will see that
304 |
305 | * it uses a function to avoid repetition.
306 | * Global variables are quarantined by collecting them together at the end, not the start of the program.
307 | * Magic numbers are avoided.
308 | * The loop at the end where the actual work is done is short and relatively simple.
309 |
310 | Exercises
311 | =========
312 |
313 | Exercise 1
314 | ----------
315 |
316 | Here is some code that needs improving.
317 |
318 | It involves a basic supply and demand problem.
319 |
320 | Supply is given by
321 |
322 | .. math:: q_s(p) = \exp(\alpha p) - \beta.
323 |
324 | The demand curve is
325 |
326 | .. math:: q_d(p) = \gamma p^{-\delta}.
327 |
328 | The values :math:`\alpha`, :math:`\beta`, :math:`\gamma` and
329 | :math:`\delta` are **parameters**
330 |
331 | The equilibrium :math:`p^*` is the price such that
332 | :math:`q_d(p) = q_s(p)`.
333 |
334 | We can solve for this equilibrium using a root finding algorithm.
335 | Specifically, we will find the :math:`p` such that :math:`h(p) = 0`,
336 | where
337 |
338 | .. math:: h(p) := q_d(p) - q_s(p)
339 |
340 | This yields the equilibrium price :math:`p^*`. From this we get the
341 | equilibrium price by :math:`q^* = q_s(p^*)`
342 |
343 | The parameter values will be
344 |
345 | - :math:`\alpha = 0.1`
346 | - :math:`\beta = 1`
347 | - :math:`\gamma = 1`
348 | - :math:`\delta = 1`
349 |
350 | .. code:: ipython3
351 |
352 | from scipy.optimize import brentq
353 |
354 | # Compute equilibrium
355 | def h(p):
356 | return p**(-1) - (np.exp(0.1 * p) - 1) # demand - supply
357 |
358 | p_star = brentq(h, 2, 4)
359 | q_star = np.exp(0.1 * p_star) - 1
360 |
361 | print(f'Equilibrium price is {p_star: .2f}')
362 | print(f'Equilibrium quantity is {q_star: .2f}')
363 |
364 | Let's also plot our results.
365 |
366 | .. code:: ipython3
367 |
368 | # Now plot
369 | grid = np.linspace(2, 4, 100)
370 | fig, ax = plt.subplots()
371 |
372 | qs = np.exp(0.1 * grid) - 1
373 | qd = grid**(-1)
374 |
375 |
376 | ax.plot(grid, qd, 'b-', lw=2, label='demand')
377 | ax.plot(grid, qs, 'g-', lw=2, label='supply')
378 |
379 | ax.set_xlabel('price')
380 | ax.set_ylabel('quantity')
381 | ax.legend(loc='upper center')
382 |
383 | plt.show()
384 |
385 | We also want to consider supply and demand shifts.
386 |
387 | For example, let's see what happens when demand shifts up, with :math:`\gamma` increasing to :math:`1.25`:
388 |
389 | .. code:: ipython3
390 |
391 | # Compute equilibrium
392 | def h(p):
393 | return 1.25 * p**(-1) - (np.exp(0.1 * p) - 1)
394 |
395 | p_star = brentq(h, 2, 4)
396 | q_star = np.exp(0.1 * p_star) - 1
397 |
398 | print(f'Equilibrium price is {p_star: .2f}')
399 | print(f'Equilibrium quantity is {q_star: .2f}')
400 |
401 | .. code:: ipython3
402 |
403 | # Now plot
404 | p_grid = np.linspace(2, 4, 100)
405 | fig, ax = plt.subplots()
406 |
407 | qs = np.exp(0.1 * p_grid) - 1
408 | qd = 1.25 * p_grid**(-1)
409 |
410 |
411 | ax.plot(grid, qd, 'b-', lw=2, label='demand')
412 | ax.plot(grid, qs, 'g-', lw=2, label='supply')
413 |
414 | ax.set_xlabel('price')
415 | ax.set_ylabel('quantity')
416 | ax.legend(loc='upper center')
417 |
418 | plt.show()
419 |
420 |
421 | Now we might consider supply shifts, but you already get the idea that there's
422 | a lot of repeated code here.
423 |
424 | Refactor and improve clarity in the code above using the principles discussed
425 | in this lecture.
426 |
427 |
428 |
429 | Solutions
430 | =========
431 |
432 | Exercise 1
433 | ----------
434 |
435 | Here's one solution, that uses a class:
436 |
437 |
438 | .. code:: ipython3
439 |
440 | class Equilibrium:
441 |
442 | def __init__(self, α=0.1, β=1, γ=1, δ=1):
443 | self.α, self.β, self.γ, self.δ = α, β, γ, δ
444 |
445 | def qs(self, p):
446 | return np.exp(self.α * p) - self.β
447 |
448 | def qd(self, p):
449 | return self.γ * p**(-self.δ)
450 |
451 | def compute_equilibrium(self):
452 | def h(p):
453 | return self.qd(p) - self.qs(p)
454 | p_star = brentq(h, 2, 4)
455 | q_star = np.exp(self.α * p_star) - self.β
456 |
457 | print(f'Equilibrium price is {p_star: .2f}')
458 | print(f'Equilibrium quantity is {q_star: .2f}')
459 |
460 | def plot_equilibrium(self):
461 | # Now plot
462 | grid = np.linspace(2, 4, 100)
463 | fig, ax = plt.subplots()
464 |
465 | ax.plot(grid, self.qd(grid), 'b-', lw=2, label='demand')
466 | ax.plot(grid, self.qs(grid), 'g-', lw=2, label='supply')
467 |
468 | ax.set_xlabel('price')
469 | ax.set_ylabel('quantity')
470 | ax.legend(loc='upper center')
471 |
472 | plt.show()
473 |
474 | Let's create an instance at the default parameter values.
475 |
476 | .. code:: ipython3
477 |
478 | eq = Equilibrium()
479 |
480 | Now we'll compute the equilibrium and plot it.
481 |
482 | .. code:: ipython3
483 |
484 | eq.compute_equilibrium()
485 |
486 | .. code:: ipython3
487 |
488 | eq.plot_equilibrium()
489 |
490 | One of the nice things about our refactored code is that, when we change
491 | parameters, we don't need to repeat ourselves:
492 |
493 | .. code:: ipython3
494 |
495 | eq.γ = 1.25
496 |
497 | .. code:: ipython3
498 |
499 | eq.compute_equilibrium()
500 |
501 | .. code:: ipython3
502 |
503 | eq.plot_equilibrium()
504 |
505 |
--------------------------------------------------------------------------------
/source/rst/need_for_speed.rst:
--------------------------------------------------------------------------------
1 | .. _speed:
2 |
3 | .. include:: /_static/includes/header.raw
4 |
5 | *******************************
6 | Python for Scientific Computing
7 | *******************************
8 |
9 | .. contents:: :depth: 2
10 |
11 | .. epigraph::
12 |
13 | "We should forget about small efficiencies, say about 97% of the time:
14 | premature optimization is the root of all evil." -- Donald Knuth
15 |
16 |
17 |
18 | Overview
19 | ========
20 |
21 | Python is extremely popular for scientific computing, due to such factors as
22 |
23 | * the accessible and flexible nature of the language itself,
24 |
25 | * the huge range of high quality scientific libraries now available,
26 |
27 | * the fact that the language and libraries are open source,
28 |
29 | * the popular Anaconda Python distribution, which simplifies installation and
30 | management of those libraries, and
31 |
32 | * the recent surge of interest in using Python for machine learning and
33 | artificial intelligence.
34 |
35 | In this lecture we give a short overview of scientific computing in Python,
36 | addressing the following questions:
37 |
38 | * What are the relative strengths and weaknesses of Python for these tasks?
39 |
40 | * What are the main elements of the scientific Python ecosystem?
41 |
42 | * How is the situation changing over time?
43 |
44 |
45 | In addition to what's in Anaconda, this lecture will need
46 |
47 | .. code-block:: ipython
48 | :class: hide-output
49 |
50 | !conda install -y quantecon
51 |
52 |
53 |
54 |
55 | Scientific Libraries
56 | =============================
57 |
58 | Let's briefly review Python's scientific libraries, starting with why we need
59 | them.
60 |
61 | The Role of Scientific Libraries
62 | --------------------------------
63 |
64 | One obvious reason we use scientific libraries is because they implement
65 | routines we want to use.
66 |
67 | For example, it's almost always better to use an existing routine for root
68 | finding than to write a new one from scratch.
69 |
70 | (For standard algorithms, efficiency is maximized if the community can coordinate on a
71 | common set of implementations, written by experts and tuned by users to be as fast and robust as possible.)
72 |
73 | But this is not the only reason that we use Python's scientific libraries.
74 |
75 | Another is that pure Python, while flexible and elegant, is not fast.
76 |
77 | So we need libraries that are designed to accelerate execution of Python code.
78 |
79 | As we'll see below, there are now Python libraries that can do this extremely well.
80 |
81 |
82 |
83 | Python's Scientific Ecosystem
84 | -----------------------------
85 |
86 |
87 | In terms of popularity, the big four in the world of scientific Python
88 | libraries are
89 |
90 | * NumPy
91 | * SciPy
92 | * Matplotlib
93 | * Pandas
94 |
95 | For us, there's another (relatively new) library that will also be essential for
96 | numerical computing:
97 |
98 | * Numba
99 |
100 | Over the next few lectures we'll see how to use these libraries.
101 |
102 | But first, let's quickly review how they fit together.
103 |
104 | * NumPy forms the foundations by providing a basic array data type (think of
105 | vectors and matrices) and functions for acting on these arrays (e.g., matrix
106 | multiplication).
107 |
108 | * SciPy builds on NumPy by adding the kinds of numerical methods that are
109 | routinely used in science (interpolation, optimization, root finding, etc.).
110 |
111 | * Matplotlib is used to generate figures, with a focus on plotting data stored in NumPy arrays.
112 |
113 | * Pandas provides types and functions for empirical work (e.g., manipulating data).
114 |
115 | * Numba accelerates execution via JIT compilation --- we'll learn about this
116 | soon.
117 |
118 |
119 |
120 |
121 |
122 | The Need for Speed
123 | ==================
124 |
125 | Now let's discuss execution speed.
126 |
127 | Higher-level languages like Python are optimized for humans.
128 |
129 | This means that the programmer can leave many details to the runtime environment
130 |
131 | * specifying variable types
132 |
133 | * memory allocation/deallocation, etc.
134 |
135 | The upside is that, compared to low-level languages, Python is typically faster to write, less error-prone and easier to debug.
136 |
137 | The downside is that Python is harder to optimize --- that is, turn into fast machine code --- than languages like C or Fortran.
138 |
139 | Indeed, the standard implementation of Python (called CPython) cannot match the speed of compiled languages such as C or Fortran.
140 |
141 | Does that mean that we should just switch to C or Fortran for everything?
142 |
143 | The answer is: No, no and one hundred times no!
144 |
145 | (This is what you should say to the senior professor insisting that the model
146 | needs to be rewritten in Fortran or C++.)
147 |
148 | There are two reasons why:
149 |
150 | First, for any given program, relatively few lines are ever going to
151 | be time-critical.
152 |
153 | Hence it is far more efficient to write most of our code in a high productivity language like Python.
154 |
155 | Second, even for those lines of code that *are* time-critical, we can now achieve the same speed as C or Fortran using Python's scientific libraries.
156 |
157 |
158 | Where are the Bottlenecks?
159 | --------------------------
160 |
161 | Before we learn how to do this, let's try to understand why plain vanilla
162 | Python is slower than C or Fortran.
163 |
164 | This will, in turn, help us figure out how to speed things up.
165 |
166 |
167 | Dynamic Typing
168 | ^^^^^^^^^^^^^^
169 |
170 | .. index::
171 | single: Dynamic Typing
172 |
173 | Consider this Python operation
174 |
175 | .. code-block:: python3
176 |
177 | a, b = 10, 10
178 | a + b
179 |
180 |
181 | Even for this simple operation, the Python interpreter has a fair bit of work to do.
182 |
183 | For example, in the statement ``a + b``, the interpreter has to know which
184 | operation to invoke.
185 |
186 | If ``a`` and ``b`` are strings, then ``a + b`` requires string concatenation
187 |
188 | .. code-block:: python3
189 |
190 | a, b = 'foo', 'bar'
191 | a + b
192 |
193 |
194 | If ``a`` and ``b`` are lists, then ``a + b`` requires list concatenation
195 |
196 | .. code-block:: python3
197 |
198 | a, b = ['foo'], ['bar']
199 | a + b
200 |
201 |
202 | (We say that the operator ``+`` is *overloaded* --- its action depends on the
203 | type of the objects on which it acts)
204 |
205 | As a result, Python must check the type of the objects and then call the correct operation.
206 |
207 | This involves substantial overheads.
208 |
209 | Static Types
210 | ^^^^^^^^^^^^
211 |
212 | .. index::
213 | single: Static Types
214 |
215 | Compiled languages avoid these overheads with explicit, static types.
216 |
217 | For example, consider the following C code, which sums the integers from 1 to 10
218 |
219 | .. code-block:: c
220 | :class: no-execute
221 |
222 | #include
223 |
224 | int main(void) {
225 | int i;
226 | int sum = 0;
227 | for (i = 1; i <= 10; i++) {
228 | sum = sum + i;
229 | }
230 | printf("sum = %d\n", sum);
231 | return 0;
232 | }
233 |
234 | The variables ``i`` and ``sum`` are explicitly declared to be integers.
235 |
236 | Hence, the meaning of addition here is completely unambiguous.
237 |
238 | Data Access
239 | -----------
240 |
241 | Another drag on speed for high-level languages is data access.
242 |
243 | To illustrate, let's consider the problem of summing some data --- say, a collection of integers.
244 |
245 | Summing with Compiled Code
246 | ^^^^^^^^^^^^^^^^^^^^^^^^^^
247 |
248 | In C or Fortran, these integers would typically be stored in an array, which
249 | is a simple data structure for storing homogeneous data.
250 |
251 | Such an array is stored in a single contiguous block of memory
252 |
253 | * In modern computers, memory addresses are allocated to each byte (one byte = 8 bits).
254 |
255 | * For example, a 64 bit integer is stored in 8 bytes of memory.
256 |
257 | * An array of :math:`n` such integers occupies :math:`8n` **consecutive** memory slots.
258 |
259 | Moreover, the compiler is made aware of the data type by the programmer.
260 |
261 | * In this case 64 bit integers
262 |
263 | Hence, each successive data point can be accessed by shifting forward in memory
264 | space by a known and fixed amount.
265 |
266 | * In this case 8 bytes
267 |
268 |
269 | Summing in Pure Python
270 | ^^^^^^^^^^^^^^^^^^^^^^
271 |
272 | Python tries to replicate these ideas to some degree.
273 |
274 | For example, in the standard Python implementation (CPython), list elements are placed in memory locations that are in a sense contiguous.
275 |
276 | However, these list elements are more like pointers to data rather than actual data.
277 |
278 | Hence, there is still overhead involved in accessing the data values themselves.
279 |
280 | This is a considerable drag on speed.
281 |
282 | In fact, it's generally true that memory traffic is a major culprit when it comes to slow execution.
283 |
284 | Let's look at some ways around these problems.
285 |
286 |
287 |
288 | :index:`Vectorization`
289 | ======================
290 |
291 | .. index::
292 | single: Python; Vectorization
293 |
294 | There is a clever method called **vectorization** that can be
295 | used to speed up high level languages in numerical applications.
296 |
297 | The key idea is to send array processing operations in batch to pre-compiled
298 | and efficient native machine code.
299 |
300 | The machine code itself is typically compiled from carefully optimized C or Fortran.
301 |
302 | For example, when working in a high level language, the operation of inverting a large matrix can be subcontracted to efficient machine code that is pre-compiled for this purpose and supplied to users as part of a package.
303 |
304 | This clever idea dates back to MATLAB, which uses vectorization extensively.
305 |
306 | Vectorization can greatly accelerate many numerical computations (but not all,
307 | as we shall see).
308 |
309 | Let's see how vectorization works in Python, using NumPy.
310 |
311 |
312 | Operations on Arrays
313 | --------------------
314 |
315 | .. index::
316 | single: Vectorization; Operations on Arrays
317 |
318 | First, let's run some imports
319 |
320 | .. code-block:: python3
321 |
322 | import random
323 | import numpy as np
324 | import quantecon as qe
325 |
326 | Next let's try some non-vectorized code, which uses a native Python loop to generate,
327 | square and then sum a large number of random variables:
328 |
329 | .. code-block:: python3
330 |
331 | n = 1_000_000
332 |
333 | .. code-block:: python3
334 |
335 | %%time
336 |
337 | y = 0 # Will accumulate and store sum
338 | for i in range(n):
339 | x = random.uniform(0, 1)
340 | y += x**2
341 |
342 | The following vectorized code achieves the same thing.
343 |
344 | .. code-block:: ipython
345 |
346 | %%time
347 |
348 | x = np.random.uniform(0, 1, n)
349 | y = np.sum(x**2)
350 |
351 |
352 | As you can see, the second code block runs much faster. Why?
353 |
354 | The second code block breaks the loop down into three basic operations
355 |
356 | #. draw ``n`` uniforms
357 |
358 | #. square them
359 |
360 | #. sum them
361 |
362 | These are sent as batch operators to optimized machine code.
363 |
364 | Apart from minor overheads associated with sending data back and forth, the result is C or Fortran-like speed.
365 |
366 | When we run batch operations on arrays like this, we say that the code is *vectorized*.
367 |
368 | Vectorized code is typically fast and efficient.
369 |
370 | It is also surprisingly flexible, in the sense that many operations can be vectorized.
371 |
372 | The next section illustrates this point.
373 |
374 |
375 |
376 |
377 |
378 | .. _ufuncs:
379 |
380 |
381 | Universal Functions
382 | -------------------
383 |
384 | .. index::
385 | single: NumPy; Universal Functions
386 |
387 | Many functions provided by NumPy are so-called *universal functions* --- also called `ufuncs `__.
388 |
389 | This means that they
390 |
391 | * map scalars into scalars, as expected
392 |
393 | * map arrays into arrays, acting element-wise
394 |
395 | For example, ``np.cos`` is a ufunc:
396 |
397 | .. code-block:: python3
398 |
399 | np.cos(1.0)
400 |
401 | .. code-block:: python3
402 |
403 | np.cos(np.linspace(0, 1, 3))
404 |
405 |
406 | By exploiting ufuncs, many operations can be vectorized.
407 |
408 | For example, consider the problem of maximizing a function :math:`f` of two
409 | variables :math:`(x,y)` over the square :math:`[-a, a] \times [-a, a]`.
410 |
411 | For :math:`f` and :math:`a` let's choose
412 |
413 | .. math::
414 |
415 | f(x,y) = \frac{\cos(x^2 + y^2)}{1 + x^2 + y^2}
416 | \quad \text{and} \quad
417 | a = 3
418 |
419 |
420 | Here's a plot of :math:`f`
421 |
422 | .. code-block:: ipython
423 |
424 | import matplotlib.pyplot as plt
425 | %matplotlib inline
426 | from mpl_toolkits.mplot3d.axes3d import Axes3D
427 | from matplotlib import cm
428 |
429 | def f(x, y):
430 | return np.cos(x**2 + y**2) / (1 + x**2 + y**2)
431 |
432 | xgrid = np.linspace(-3, 3, 50)
433 | ygrid = xgrid
434 | x, y = np.meshgrid(xgrid, ygrid)
435 |
436 | fig = plt.figure(figsize=(8, 6))
437 | ax = fig.add_subplot(111, projection='3d')
438 | ax.plot_surface(x,
439 | y,
440 | f(x, y),
441 | rstride=2, cstride=2,
442 | cmap=cm.jet,
443 | alpha=0.7,
444 | linewidth=0.25)
445 | ax.set_zlim(-0.5, 1.0)
446 | ax.set_xlabel('$x$', fontsize=14)
447 | ax.set_ylabel('$y$', fontsize=14)
448 | plt.show()
449 |
450 | To maximize it, we're going to use a naive grid search:
451 |
452 | #. Evaluate :math:`f` for all :math:`(x,y)` in a grid on the square.
453 |
454 | #. Return the maximum of observed values.
455 |
456 | The grid will be
457 |
458 | .. code-block:: python3
459 |
460 | grid = np.linspace(-3, 3, 1000)
461 |
462 | Here's a non-vectorized version that uses Python loops.
463 |
464 | .. code-block:: python3
465 |
466 | %%time
467 |
468 | m = -np.inf
469 |
470 | for x in grid:
471 | for y in grid:
472 | z = f(x, y)
473 | if z > m:
474 | m = z
475 |
476 |
477 | And here's a vectorized version
478 |
479 | .. code-block:: python3
480 |
481 | %%time
482 |
483 | x, y = np.meshgrid(grid, grid)
484 | np.max(f(x, y))
485 |
486 |
487 | In the vectorized version, all the looping takes place in compiled code.
488 |
489 | As you can see, the second version is **much** faster.
490 |
491 | (We'll make it even faster again later on, using more scientific programming tricks.)
492 |
493 |
494 |
495 | .. _numba-p_c_vectorization:
496 |
497 | Beyond Vectorization
498 | ====================
499 |
500 |
501 | At its best, vectorization yields fast, simple code.
502 |
503 | However, it's not without disadvantages.
504 |
505 | One issue is that it can be highly memory-intensive.
506 |
507 | For example, the vectorized maximization routine above is far more memory
508 | intensive than the non-vectorized version that preceded it.
509 |
510 | This is because vectorization tends to create many intermediate arrays before
511 | producing the final calculation.
512 |
513 | Another issue is that not all algorithms can be vectorized.
514 |
515 | In these kinds of settings, we need to go back to loops.
516 |
517 | Fortunately, there are alternative ways to speed up Python loops that work in
518 | almost any setting.
519 |
520 | For example, in the last few years, a new Python library called `Numba
521 | `__ has appeared that solves the main problems
522 | with vectorization listed above.
523 |
524 | It does so through something called **just in time (JIT) compilation**,
525 | which can generate extremely fast and efficient code.
526 |
527 | We'll learn how to use Numba :doc:`soon `.
528 |
--------------------------------------------------------------------------------
/source/rst/parallelization.rst:
--------------------------------------------------------------------------------
1 | .. _parallel:
2 |
3 | .. include:: /_static/includes/header.raw
4 |
5 | ***************
6 | Parallelization
7 | ***************
8 |
9 | .. contents:: :depth: 2
10 |
11 | In addition to what's in Anaconda, this lecture will need the following libraries:
12 |
13 | .. code-block:: ipython
14 | :class: hide-output
15 |
16 | !conda install -y quantecon
17 |
18 |
19 | Overview
20 | ========
21 |
22 |
23 | The growth of CPU clock speed (i.e., the speed at which a single chain of logic can
24 | be run) has slowed dramatically in recent years.
25 |
26 | This is unlikely to change in the near future, due to inherent physical
27 | limitations on the construction of chips and circuit boards.
28 |
29 | Chip designers and computer programmers have responded to the slowdown by
30 | seeking a different path to fast execution: parallelization.
31 |
32 | Hardware makers have increased the number of cores (physical CPUs) embedded in each machine.
33 |
34 | For programmers, the challenge has been to exploit these multiple CPUs by running many processes in parallel (i.e., simultaneously).
35 |
36 | This is particularly important in scientific programming, which requires handling
37 |
38 | * large amounts of data and
39 |
40 | * CPU intensive simulations and other calculations.
41 |
42 | In this lecture we discuss parallelization for scientific computing, with a focus on
43 |
44 | #. the best tools for parallelization in Python and
45 |
46 | #. how these tools can be applied to quantitative economic problems.
47 |
48 |
49 | Let's start with some imports:
50 |
51 | .. code-block:: ipython
52 |
53 | import numpy as np
54 | import quantecon as qe
55 | import matplotlib.pyplot as plt
56 |
57 | %matplotlib inline
58 |
59 |
60 | Types of Parallelization
61 | ========================
62 |
63 | Large textbooks have been written on different approaches to parallelization but we will keep a tight focus on what's most useful to us.
64 |
65 | We will briefly review the two main kinds of parallelization commonly used in
66 | scientific computing and discuss their pros and cons.
67 |
68 |
69 | Multiprocessing
70 | ---------------
71 |
72 | Multiprocessing means concurrent execution of multiple processes using more than one processor.
73 |
74 | In this context, a **process** is a chain of instructions (i.e., a program).
75 |
76 | Multiprocessing can be carried out on one machine with multiple CPUs or on a
77 | collection of machines connected by a network.
78 |
79 | In the latter case, the collection of machines is usually called a
80 | **cluster**.
81 |
82 | With multiprocessing, each process has its own memory space, although the
83 | physical memory chip might be shared.
84 |
85 |
86 | Multithreading
87 | --------------
88 |
89 | Multithreading is similar to multiprocessing, except that, during execution, the threads all share the same memory space.
90 |
91 | Native Python struggles to implement multithreading due to some `legacy design
92 | features `__.
93 |
94 | But this is not a restriction for scientific libraries like NumPy and Numba.
95 |
96 | Functions imported from these libraries and JIT-compiled code run in low level
97 | execution environments where Python's legacy restrictions don't apply.
98 |
99 |
100 | Advantages and Disadvantages
101 | ----------------------------
102 |
103 | Multithreading is more lightweight because most system and memory resources
104 | are shared by the threads.
105 |
106 | In addition, the fact that multiple threads all access a shared pool of memory
107 | is extremely convenient for numerical programming.
108 |
109 | On the other hand, multiprocessing is more flexible and can be distributed
110 | across clusters.
111 |
112 | For the great majority of what we do in these lectures, multithreading will
113 | suffice.
114 |
115 |
116 | Implicit Multithreading in NumPy
117 | ================================
118 |
119 | Actually, you have already been using multithreading in your Python code,
120 | although you might not have realized it.
121 |
122 | (We are, as usual, assuming that you are running the latest version of
123 | Anaconda Python.)
124 |
125 | This is because NumPy cleverly implements multithreading in a lot of its
126 | compiled code.
127 |
128 | Let's look at some examples to see this in action.
129 |
130 | A Matrix Operation
131 | ------------------
132 |
133 | The next piece of code computes the eigenvalues of a large number of randomly
134 | generated matrices.
135 |
136 | It takes a few seconds to run.
137 |
138 | .. code-block:: python3
139 |
140 | n = 20
141 | m = 1000
142 | for i in range(n):
143 | X = np.random.randn(m, m)
144 | λ = np.linalg.eigvals(X)
145 |
146 | Now, let's look at the output of the `htop` system monitor on our machine while
147 | this code is running:
148 |
149 | .. figure:: /_static/lecture_specific/parallelization/htop_parallel_npmat.png
150 | :scale: 45%
151 |
152 | We can see that 4 of the 8 CPUs are running at full speed.
153 |
154 |
155 | This is because NumPy's ``eigvals`` routine neatly splits up the tasks and
156 | distributes them to different threads.
157 |
158 |
159 | A Multithreaded Ufunc
160 | ---------------------
161 |
162 | Over the last few years, NumPy has managed to push this kind of multithreading
163 | out to more and more operations.
164 |
165 | For example, let's return to a maximization problem :ref:`discussed previously `:
166 |
167 | .. code-block:: python3
168 |
169 | def f(x, y):
170 | return np.cos(x**2 + y**2) / (1 + x**2 + y**2)
171 |
172 | grid = np.linspace(-3, 3, 5000)
173 | x, y = np.meshgrid(grid, grid)
174 |
175 | .. code-block:: ipython3
176 |
177 | %timeit np.max(f(x, y))
178 |
179 | If you have a system monitor such as `htop` (Linux/Mac) or `perfmon`
180 | (Windows), then try running this and then observing the load on your CPUs.
181 |
182 | (You will probably need to bump up the grid size to see large effects.)
183 |
184 | At least on our machine, the output shows that the operation is successfully
185 | distributed across multiple threads.
186 |
187 | This is one of the reasons why the vectorized code above is fast.
188 |
189 | A Comparison with Numba
190 | -----------------------
191 |
192 | To get some basis for comparison for the last example, let's try the same
193 | thing with Numba.
194 |
195 | In fact there is an easy way to do this, since Numba can also be used to
196 | create custom :ref:`ufuncs ` with the `@vectorize
197 | `__ decorator.
198 |
199 | .. code-block:: python3
200 |
201 | from numba import vectorize
202 |
203 | @vectorize
204 | def f_vec(x, y):
205 | return np.cos(x**2 + y**2) / (1 + x**2 + y**2)
206 |
207 | np.max(f_vec(x, y)) # Run once to compile
208 |
209 | .. code-block:: ipython3
210 |
211 | %timeit np.max(f_vec(x, y))
212 |
213 | At least on our machine, the difference in the speed between the
214 | Numba version and the vectorized NumPy version shown above is not large.
215 |
216 | But there's quite a bit going on here so let's try to break down what is
217 | happening.
218 |
219 | Both Numba and NumPy use efficient machine code that's specialized to these
220 | floating point operations.
221 |
222 | However, the code NumPy uses is, in some ways, less efficient.
223 |
224 | The reason is that, in NumPy, the operation ``np.cos(x**2 + y**2) / (1 +
225 | x**2 + y**2)`` generates several intermediate arrays.
226 |
227 | For example, a new array is created when ``x**2`` is calculated.
228 |
229 | The same is true when ``y**2`` is calculated, and then ``x**2 + y**2`` and so on.
230 |
231 | Numba avoids creating all these intermediate arrays by compiling one
232 | function that is specialized to the entire operation.
233 |
234 | But if this is true, then why isn't the Numba code faster?
235 |
236 | The reason is that NumPy makes up for its disadvantages with implicit
237 | multithreading, as we've just discussed.
238 |
239 | Multithreading a Numba Ufunc
240 | ----------------------------
241 |
242 | Can we get both of these advantages at once?
243 |
244 | In other words, can we pair
245 |
246 | * the efficiency of Numba's highly specialized JIT compiled function and
247 |
248 | * the speed gains from parallelization obtained by NumPy's implicit
249 | multithreading?
250 |
251 | It turns out that we can, by adding some type information plus ``target='parallel'``.
252 |
253 | .. code-block:: python3
254 |
255 | @vectorize('float64(float64, float64)', target='parallel')
256 | def f_vec(x, y):
257 | return np.cos(x**2 + y**2) / (1 + x**2 + y**2)
258 |
259 | np.max(f_vec(x, y)) # Run once to compile
260 |
261 | .. code-block:: ipython3
262 |
263 | %timeit np.max(f_vec(x, y))
264 |
265 | Now our code runs significantly faster than the NumPy version.
266 |
267 |
268 |
269 | Multithreaded Loops in Numba
270 | ============================
271 |
272 | We just saw one approach to parallelization in Numba, using the ``parallel``
273 | flag in ``@vectorize``.
274 |
275 | This is neat but, it turns out, not well suited to many problems we consider.
276 |
277 | Fortunately, Numba provides another approach to multithreading that will work
278 | for us almost everywhere parallelization is possible.
279 |
280 | To illustrate, let's look first at a simple, single-threaded (i.e., non-parallelized) piece of code.
281 |
282 | The code simulates updating the wealth :math:`w_t` of a household via the rule
283 |
284 | .. math::
285 |
286 | w_{t+1} = R_{t+1} s w_t + y_{t+1}
287 |
288 | Here
289 |
290 | * :math:`R` is the gross rate of return on assets
291 | * :math:`s` is the savings rate of the household and
292 | * :math:`y` is labor income.
293 |
294 | We model both :math:`R` and :math:`y` as independent draws from a lognormal
295 | distribution.
296 |
297 | Here's the code:
298 |
299 | .. code-block:: ipython
300 |
301 | from numpy.random import randn
302 | from numba import njit
303 |
304 | @njit
305 | def h(w, r=0.1, s=0.3, v1=0.1, v2=1.0):
306 | """
307 | Updates household wealth.
308 | """
309 |
310 | # Draw shocks
311 | R = np.exp(v1 * randn()) * (1 + r)
312 | y = np.exp(v2 * randn())
313 |
314 | # Update wealth
315 | w = R * s * w + y
316 | return w
317 |
318 |
319 | Let's have a look at how wealth evolves under this rule.
320 |
321 | .. code-block:: ipython
322 |
323 | fig, ax = plt.subplots()
324 |
325 | T = 100
326 | w = np.empty(T)
327 | w[0] = 5
328 | for t in range(T-1):
329 | w[t+1] = h(w[t])
330 |
331 | ax.plot(w)
332 | ax.set_xlabel('$t$', fontsize=12)
333 | ax.set_ylabel('$w_{t}$', fontsize=12)
334 | plt.show()
335 |
336 | Now let's suppose that we have a large population of households and we want to
337 | know what median wealth will be.
338 |
339 | This is not easy to solve with pencil and paper, so we will use simulation
340 | instead.
341 |
342 | In particular, we will simulate a large number of households and then
343 | calculate median wealth for this group.
344 |
345 | Suppose we are interested in the long-run average of this median over time.
346 |
347 | It turns out that, for the specification that we've chosen above, we can
348 | calculate this by taking a one-period snapshot of what has happened to median
349 | wealth of the group at the end of a long simulation.
350 |
351 | Moreover, provided the simulation period is long enough, initial conditions
352 | don't matter.
353 |
354 | * This is due to something called ergodicity, which we will discuss `later on `_.
355 |
356 | So, in summary, we are going to simulate 50,000 households by
357 |
358 | #. arbitrarily setting initial wealth to 1 and
359 |
360 | #. simulating forward in time for 1,000 periods.
361 |
362 | Then we'll calculate median wealth at the end period.
363 |
364 | Here's the code:
365 |
366 | .. code-block:: ipython
367 |
368 | @njit
369 | def compute_long_run_median(w0=1, T=1000, num_reps=50_000):
370 |
371 | obs = np.empty(num_reps)
372 | for i in range(num_reps):
373 | w = w0
374 | for t in range(T):
375 | w = h(w)
376 | obs[i] = w
377 |
378 | return np.median(obs)
379 |
380 | Let's see how fast this runs:
381 |
382 | .. code-block:: ipython
383 |
384 | %%time
385 | compute_long_run_median()
386 |
387 |
388 | To speed this up, we're going to parallelize it via multithreading.
389 |
390 | To do so, we add the ``parallel=True`` flag and change ``range`` to ``prange``:
391 |
392 | .. code-block:: ipython
393 |
394 | from numba import prange
395 |
396 | @njit(parallel=True)
397 | def compute_long_run_median_parallel(w0=1, T=1000, num_reps=50_000):
398 |
399 | obs = np.empty(num_reps)
400 | for i in prange(num_reps):
401 | w = w0
402 | for t in range(T):
403 | w = h(w)
404 | obs[i] = w
405 |
406 | return np.median(obs)
407 |
408 | Let's look at the timing:
409 |
410 | .. code-block:: ipython
411 |
412 | %%time
413 | compute_long_run_median_parallel()
414 |
415 | The speed-up is significant.
416 |
417 | A Warning
418 | ---------
419 |
420 | Parallelization works well in the outer loop of the last example because the individual tasks inside the loop are independent of each other.
421 |
422 | If this independence fails then parallelization is often problematic.
423 |
424 | For example, each step inside the inner loop depends on the last step, so
425 | independence fails, and this is why we use ordinary ``range`` instead of ``prange``.
426 |
427 | When you see us using ``prange`` in later lectures, it is because the
428 | independence of tasks holds true.
429 |
430 | When you see us using ordinary ``range`` in a jitted function, it is either because the speed gain from parallelization is small or because independence fails.
431 |
432 | .. Dask
433 |
434 | .. To be added.
435 |
436 |
437 | .. GPUs
438 |
439 | .. Just say a few words about them. How do they relate to the foregoing? Explain that we can't introduce executable GPU code here.
440 |
441 |
442 | Exercises
443 | =========
444 |
445 | Exercise 1
446 | ----------
447 |
448 | In :ref:`an earlier exercise `, we used Numba to accelerate an
449 | effort to compute the constant :math:`\pi` by Monte Carlo.
450 |
451 | Now try adding parallelization and see if you get further speed gains.
452 |
453 | You should not expect huge gains here because, while there are many
454 | independent tasks (draw point and test if in circle), each one has low
455 | execution time.
456 |
457 | Generally speaking, parallelization is less effective when the individual
458 | tasks to be parallelized are very small relative to total execution time.
459 |
460 | This is due to overheads associated with spreading all of these small tasks across multiple CPUs.
461 |
462 | Nevertheless, with suitable hardware, it is possible to get nontrivial speed gains in this exercise.
463 |
464 | For the size of the Monte Carlo simulation, use something substantial, such as
465 | ``n = 100_000_000``.
466 |
467 |
468 | Solutions
469 | =========
470 |
471 | Exercise 1
472 | ----------
473 |
474 | Here is one solution:
475 |
476 | .. code-block:: python3
477 |
478 | from random import uniform
479 |
480 | @njit(parallel=True)
481 | def calculate_pi(n=1_000_000):
482 | count = 0
483 | for i in prange(n):
484 | u, v = uniform(0, 1), uniform(0, 1)
485 | d = np.sqrt((u - 0.5)**2 + (v - 0.5)**2)
486 | if d < 0.5:
487 | count += 1
488 |
489 | area_estimate = count / n
490 | return area_estimate * 4 # dividing by radius**2
491 |
492 | Now let's see how fast it runs:
493 |
494 | .. code-block:: ipython3
495 |
496 | %time calculate_pi()
497 |
498 | .. code-block:: ipython3
499 |
500 | %time calculate_pi()
501 |
502 | By switching parallelization on and off (selecting ``True`` or
503 | ``False`` in the ``@njit`` annotation), we can test the speed gain that
504 | multithreading provides on top of JIT compilation.
505 |
506 | On our workstation, we find that parallelization increases execution speed by
507 | a factor of 2 or 3.
508 |
509 | (If you are executing locally, you will get different numbers, depending mainly
510 | on the number of CPUs on your machine.)
511 |
512 |
513 |
514 |
515 |
--------------------------------------------------------------------------------
/conf.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | #
4 | # QuantEcon.lectures-python documentation build configuration file, created by
5 | # sphinx-quickstart on Mon Feb 13 14:28:35 2017.
6 | #
7 | # This file is execfile()d with the current directory set to its
8 | # containing dir.
9 | #
10 | # Note that not all possible configuration values are present in this
11 | # autogenerated file.
12 | #
13 | # All configuration values have a default; values that are commented out
14 | # serve to show the default.
15 |
16 | import sys
17 | import os
18 | import nbformat
19 | import datetime
20 |
21 | now = datetime.datetime.now()
22 |
23 |
24 | # If extensions (or modules to document with autodoc) are in another directory,
25 | # add these directories to sys.path here. If the directory is relative to the
26 | # documentation root, use os.path.abspath to make it absolute, like shown here.
27 | #sys.path.insert(0, os.path.abspath('.'))
28 |
29 | # -- General configuration ------------------------------------------------
30 |
31 | # If your documentation needs a minimal Sphinx version, state it here.
32 | needs_sphinx = '1.5'
33 |
34 | # Add any Sphinx extension module names here, as strings. They can be
35 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
36 | # ones.
37 | extensions = [
38 | 'sphinx.ext.mathjax',
39 | 'sphinxcontrib.bibtex',
40 | 'IPython.sphinxext.ipython_console_highlighting',
41 | # Custom Sphinx Extensions
42 | 'sphinxcontrib.jupyter',
43 | ]
44 |
45 | # Retired Extensions but may be useful in Future
46 |
47 | # 'matplotlib.sphinxext.plot_directive',
48 | # 'matplotlib.sphinxext.only_directives',
49 | # 'sphinxcontrib.tikz',
50 | # 'sphinx.ext.graphviz',
51 |
52 | # Add any paths that contain templates here, relative to this directory.
53 | templates_path = ['_templates']
54 |
55 | # The suffix(es) of source filenames.
56 | # You can specify multiple suffix as a list of string:
57 | # source_suffix = ['.rst', '.md']
58 | source_suffix = '.rst'
59 |
60 | # The encoding of source files.
61 | #source_encoding = 'utf-8-sig'
62 |
63 | # The master toctree document.
64 | master_doc = 'index'
65 | master_pdf_doc = 'index'
66 |
67 | # General information about the project.
68 | project = 'QuantEcon.lectures-python3'
69 | copyright = '2019, Thomas J. Sargent and John Stachurski'
70 | author = 'Thomas J. Sargent and John Stachurski'
71 |
72 | # The version info for the project you're documenting, acts as replacement for
73 | # |version| and |release|, also used in various other places throughout the
74 | # built documents.
75 | #
76 | # The short X.Y version.
77 | version = '%s-%s-%s' % (now.year, now.strftime("%b"), now.day)
78 | # The full version, including alpha/beta/rc tags.
79 | release = version
80 |
81 | # The language for content autogenerated by Sphinx. Refer to documentation
82 | # for a list of supported languages.
83 | #
84 | # This is also used if you do content translation via gettext catalogs.
85 | # Usually you set "language" from the command line for these cases.
86 | language = None
87 |
88 | # There are two options for replacing |today|: either, you set today to some
89 | # non-false value, then it is used:
90 | #today = ''
91 | # Else, today_fmt is used as the format for a strftime call.
92 | #today_fmt = '%B %d, %Y'
93 |
94 | # List of patterns, relative to source directory, that match files and
95 | # directories to ignore when looking for source files.
96 | exclude_patterns = ['_build', '_static']
97 |
98 | # The reST default role (used for this markup: `text`) to use for all
99 | # documents.
100 | #default_role = None
101 |
102 | # If true, '()' will be appended to :func: etc. cross-reference text.
103 | #add_function_parentheses = True
104 |
105 | # If true, the current module name will be prepended to all description
106 | # unit titles (such as .. function::).
107 | #add_module_names = True
108 |
109 | # If true, sectionauthor and moduleauthor directives will be shown in the
110 | # output. They are ignored by default.
111 | #show_authors = False
112 |
113 | # The name of the Pygments (syntax highlighting) style to use.
114 | pygments_style = 'sphinx'
115 |
116 | # A list of ignored prefixes for module index sorting.
117 | #modindex_common_prefix = []
118 |
119 | # If true, keep warnings as "system message" paragraphs in the built documents.
120 | #keep_warnings = False
121 |
122 | # If true, `todo` and `todoList` produce output, else they produce nothing.
123 | todo_include_todos = False
124 |
125 | # Add rst prolog
126 | rst_prolog = """
127 | .. highlight:: python3
128 | """
129 |
130 | # -- Options for HTML output ----------------------------------------------
131 |
132 | # The theme to use for HTML and HTML Help pages. See the documentation for
133 | # a list of builtin themes.
134 | html_theme = 'qe-lectures'
135 |
136 | # Theme options are theme-specific and customize the look and feel of a theme
137 | # further. For a list of options available for each theme, see the
138 | # documentation.
139 | #html_theme_options = {}
140 |
141 | # Add any paths that contain custom themes here, relative to this directory.
142 | html_theme_path = ['_themes']
143 |
144 | # The name for this set of Sphinx documents. If None, it defaults to
145 | # " v documentation".
146 | html_title = "Quantitative Economics with Python"
147 |
148 | # A shorter title for the navigation bar. Default is the same as html_title.
149 | #html_short_title = None
150 |
151 | # The name of an image file (relative to this directory) to place at the top
152 | # of the sidebar.
153 | #html_logo = None
154 |
155 | # The name of an image file (within the static path) to use as favicon of the
156 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
157 | # pixels large.
158 | #html_favicon = None
159 |
160 | # Add any paths that contain custom static files (such as style sheets) here,
161 | # relative to this directory. They are copied after the builtin static files,
162 | # so a file named "default.css" will overwrite the builtin "default.css".
163 | html_static_path = ['_static']
164 |
165 | # Add any extra paths that contain custom files (such as robots.txt or
166 | # .htaccess) here, relative to this directory. These files are copied
167 | # directly to the root of the documentation.
168 | #html_extra_path = []
169 |
170 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
171 | # using the given strftime format.
172 | #html_last_updated_fmt = '%b %d, %Y'
173 |
174 | # If true, SmartyPants will be used to convert quotes and dashes to
175 | # typographically correct entities.
176 | #html_use_smartypants = True
177 |
178 | # Custom sidebar templates, maps document names to template names.
179 | # html_sidebars = {
180 | # 'index': ['py_layout.html'],
181 | # }
182 |
183 | # Additional templates that should be rendered to pages, maps page names to
184 | # template names.
185 | #html_additional_pages = {}
186 |
187 | # If false, no module index is generated.
188 | #html_domain_indices = True
189 |
190 | # If false, no index is generated.
191 | #html_use_index = True
192 |
193 | # If true, the index is split into individual pages for each letter.
194 | #html_split_index = False
195 |
196 | # If true, links to the reST sources are added to the pages.
197 | #html_show_sourcelink = True
198 |
199 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
200 | #html_show_sphinx = True
201 |
202 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
203 | #html_show_copyright = True
204 |
205 | # If true, an OpenSearch description file will be output, and all pages will
206 | # contain a tag referring to it. The value of this option must be the
207 | # base URL from which the finished HTML is served.
208 | #html_use_opensearch = ''
209 |
210 | # This is the file name suffix for HTML files (e.g. ".xhtml").
211 | #html_file_suffix = None
212 |
213 | # Language to be used for generating the HTML full-text search index.
214 | # Sphinx supports the following languages:
215 | # 'da', 'de', 'en', 'es', 'fi', 'fr', 'h', 'it', 'ja'
216 | # 'nl', 'no', 'pt', 'ro', 'r', 'sv', 'tr'
217 | #html_search_language = 'en'
218 |
219 | # A dictionary with options for the search language support, empty by default.
220 | # Now only 'ja' uses this config value
221 | #html_search_options = {'type': 'default'}
222 |
223 | # The name of a javascript file (relative to the configuration directory) that
224 | # implements a search results scorer. If empty, the default will be used.
225 | #html_search_scorer = 'scorer.js'
226 |
227 | # Output file base name for HTML help builder.
228 | htmlhelp_basename = 'QuantEconlectures-python3doc'
229 |
230 | # Tikz HTML configuration for rendering images
231 | tikz_latex_preamble = r"""
232 | \usetikzlibrary{arrows}
233 | \usetikzlibrary{calc}
234 | \usetikzlibrary{intersections}
235 | \usetikzlibrary{decorations}
236 | \usetikzlibrary{decorations.pathreplacing}
237 | """
238 |
239 | # -- Options for LaTeX output ---------------------------------------------
240 |
241 | latex_elements = {
242 | # The paper size ('letterpaper' or 'a4paper').
243 | #'papersize': 'letterpaper',
244 |
245 | # The font size ('10pt', '11pt' or '12pt').
246 | 'pointsize': '11pt',
247 |
248 | # Additional stuff for the LaTeX preamble.
249 | 'preamble': r"""
250 | \usepackage{amsmath, amssymb}
251 | \usepackage{mathrsfs}
252 |
253 | \usepackage{tikz}
254 | \usetikzlibrary{arrows}
255 | \usetikzlibrary{calc}
256 | \usetikzlibrary{intersections}
257 | \usetikzlibrary{decorations}
258 | \usepackage{pgf}
259 | \usepackage{pgfplots}
260 |
261 |
262 | \usepackage{bbm}
263 | \newcommand{\RR}{\mathbbm R}
264 | \newcommand{\NN}{\mathbbm N}
265 | \newcommand{\PP}{\mathbbm P}
266 | \newcommand{\EE}{\mathbbm E \,}
267 | \newcommand{\XX}{\mathbbm X}
268 | \newcommand{\ZZ}{\mathbbm Z}
269 | \newcommand{\QQ}{\mathbbm Q}
270 |
271 | \newcommand{\fF}{\mathcal F}
272 | \newcommand{\dD}{\mathcal D}
273 | \newcommand{\lL}{\mathcal L}
274 | \newcommand{\gG}{\mathcal G}
275 | \newcommand{\hH}{\mathcal H}
276 | \newcommand{\nN}{\mathcal N}
277 | \newcommand{\pP}{\mathcal P}
278 |
279 | \DeclareMathOperator{\trace}{trace}
280 | \DeclareMathOperator{\Var}{Var}
281 | \DeclareMathOperator{\Span}{span}
282 | \DeclareMathOperator{\proj}{proj}
283 | \DeclareMathOperator{\col}{col}
284 | \DeclareMathOperator*{\argmax}{arg\,max}
285 | \DeclareMathOperator*{\argmin}{arg\,min}
286 |
287 | \usepackage{makeidx}
288 | \makeindex
289 | """,
290 |
291 | # Latex figure (float) alignment (Could use 'H' to force the placement of figures)
292 | 'figure_align': 'H',#'htbp',
293 |
294 | #Add Frontmatter before TOC
295 | 'tableofcontents' : r"""\newpage
296 | \thispagestyle{empty}
297 | \chapter*{Preface}
298 | \large
299 | This \textbf{pdf} presents a series of lectures on quantitative economic
300 | modeling, designed and written by \href{http://www.tomsargent.com/}{Thomas J. Sargent} and \href{http://johnstachurski.net}{John Stachurski}.
301 | The primary programming languages are \href{https://www.python.org}{Python} and \href{http://julialang.org/}{Julia}.
302 | You can send feedback to the authors via contact@quantecon.org.
303 |
304 | \vspace{5em}
305 |
306 | \begin{leftbar}
307 | \textbf{Note: You are currently viewing an automatically generated
308 | pdf version of our online lectures,} which are located at
309 |
310 | \vspace{2em}
311 |
312 | \begin{center}
313 | \texttt{https://lectures.quantecon.org}
314 | \end{center}
315 |
316 | \vspace{2em}
317 |
318 | Please visit the website for more information on the aims and scope of the
319 | lectures and the two language options (Julia or Python).
320 |
321 | \vspace{1em}
322 |
323 | Due to automatic generation of this pdf, \textbf{presentation quality is likely
324 | to be lower than that of the website}.
325 |
326 | \end{leftbar}
327 |
328 | \normalsize
329 |
330 | \sphinxtableofcontents
331 | """
332 | }
333 |
334 | # Grouping the document tree into LaTeX files. List of tuples
335 | # (source start file, target name, title,
336 | # author, documentclass [howto, manual, or own class]).
337 | latex_documents = [
338 | (master_pdf_doc, 'QuantEconlectures-python3.tex', 'QuantEcon.lectures-python3 PDF',
339 | 'Thomas J. Sargent and John Stachurski', 'manual'),
340 | ]
341 |
342 | # The name of an image file (relative to this directory) to place at the top of
343 | # the title page.
344 | #latex_logo = None
345 |
346 | # For "manual" documents, if this is true, then toplevel headings are parts,
347 | # not chapters.
348 | #latex_use_parts = False
349 |
350 | # If true, show page references after internal links.
351 | #latex_show_pagerefs = False
352 |
353 | # If true, show URL addresses after external links.
354 | #latex_show_urls = False
355 |
356 | # Documents to append as an appendix to all manuals.
357 | #latex_appendices = []
358 |
359 | # If false, no module index is generated.
360 | #latex_domain_indices = True
361 |
362 | # ------------------
363 | # Linkcheck Options
364 | # ------------------
365 |
366 | linkcheck_ignore = [r'https:\/\/github\.com\/.*?#.*'] #Anchors on Github seem to create issues with linkchecker
367 |
368 | linkcheck_timeout = 30
369 |
370 | # --------------------------------------------
371 | # jupyter Sphinx Extension conversion settings
372 | # --------------------------------------------
373 |
374 | # Conversion Mode Settings
375 | # If "all", convert codes and texts into notebook
376 | # If "code", convert codes only
377 | jupyter_conversion_mode = "all"
378 |
379 | jupyter_write_metadata = False
380 |
381 | # Location for _static folder
382 | jupyter_static_file_path = ["source/_static", "_static"]
383 |
384 | # Configure Jupyter Kernels
385 | jupyter_kernels = {
386 | "python3": {
387 | "kernelspec": {
388 | "display_name": "Python",
389 | "language": "python3",
390 | "name": "python3"
391 | },
392 | "file_extension": ".py",
393 | },
394 | }
395 |
396 | # Configure jupyter headers
397 | jupyter_headers = {
398 | "python3": [
399 | # nbformat.v4.new_code_cell("%autosave 0") #@mmcky please make this an option
400 | ],
401 | "julia": [
402 | ],
403 | }
404 |
405 | # Filename for the file containing the welcome block
406 | jupyter_welcome_block = ""
407 |
408 | #Adjust links to target html (rather than ipynb)
409 | jupyter_target_html = False
410 |
411 | #path to download notebooks from
412 | jupyter_download_nb_urlpath = None
413 |
414 | #allow downloading of notebooks
415 | jupyter_download_nb = False
416 |
417 | #Use urlprefix images
418 | jupyter_download_nb_image_urlpath = None
419 |
420 | #Allow ipython as a language synonym for blocks to be ipython highlighted
421 | jupyter_lang_synonyms = ["ipython", "ipython3"]
422 |
423 | #Execute skip-test code blocks for rendering of website (this will need to be ignored in coverage testing)
424 | jupyter_ignore_skip_test = True
425 |
426 | #allow execution of notebooks
427 | jupyter_execute_notebooks = False
428 |
429 | # Location of template folder for coverage reports
430 | jupyter_template_coverage_file_path = False
431 |
432 | # generate html from IPYNB files
433 | jupyter_generate_html = False
434 |
435 | # html template specific to your website needs
436 | jupyter_html_template = ""
437 |
438 | # latex template specific to your website needs
439 | jupyter_latex_template = ""
440 |
441 | #make website
442 | jupyter_make_site = False
443 |
444 | #force markdown image inclusion
445 | jupyter_images_markdown = True
446 |
447 | #This is set true by default to pass html to the notebooks
448 | jupyter_allow_html_only=True
449 |
450 | ## Theme specific variables
451 | # jupyter_theme_path = 'theme/minimal' #Specified in Makefile
452 | # jupyter_template_path = 'theme/minimal/templates' #Specified in Makefile
453 |
454 | jupyter_nextprev_ignore = ['404', "search", "status", "troubleshooting"]
455 |
456 | ### pdf options
457 | jupyter_pdf_logo = "_static/img/qe-menubar-logo.png"
458 |
459 | jupyter_bib_file = "_static/quant-econ"
460 |
461 | jupyter_pdf_author = "Thomas J. Sargent and John Stachurski"
462 |
463 | # Exclude Document Patterns for PDF Construction
464 | jupyter_pdf_excludepatterns = ["404", "index", "references"]
465 |
466 | # Set urlpath for html links in documents
467 | jupyter_pdf_urlpath = "https://python-programming.quantecon.org/"
468 |
469 | # make book
470 | jupyter_pdf_book = False
471 |
472 | # book title
473 | jupyter_pdf_book_title = "Python Programming for Economics and Finance"
474 |
475 | # pdf book name
476 | jupyter_pdf_book_name = "python_programming_for_economics_finance"
477 |
478 | # pdf toc file
479 | jupyter_pdf_book_index = "index_toc"
480 |
--------------------------------------------------------------------------------