From 6062225b4c0186b923538b781e87de841dd9175d Mon Sep 17 00:00:00 2001 From: mmcky Date: Mon, 21 Jul 2025 12:17:35 +1000 Subject: [PATCH 1/8] MAIN: Update to anaconda=2025.06 --- .github/workflows/cache.yml | 2 +- .github/workflows/ci.yml | 2 +- .github/workflows/publish.yml | 2 +- environment.yml | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/cache.yml b/.github/workflows/cache.yml index e2f45418c..6d6853f4e 100644 --- a/.github/workflows/cache.yml +++ b/.github/workflows/cache.yml @@ -15,7 +15,7 @@ jobs: auto-update-conda: true auto-activate-base: true miniconda-version: 'latest' - python-version: "3.12" + python-version: "3.13" environment-file: environment.yml activate-environment: quantecon - name: graphviz Support # TODO: required? diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 02ad4edf5..34c95d46e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,7 +12,7 @@ jobs: auto-update-conda: true auto-activate-base: true miniconda-version: 'latest' - python-version: "3.12" + python-version: "3.13" environment-file: environment.yml activate-environment: quantecon - name: Graphics Support #TODO: Review if graphviz is needed diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 6d195b53d..010ae3274 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -16,7 +16,7 @@ jobs: auto-update-conda: true auto-activate-base: true miniconda-version: 'latest' - python-version: "3.12" + python-version: "3.13" environment-file: environment.yml activate-environment: quantecon - name: Install latex dependencies diff --git a/environment.yml b/environment.yml index 2602bab0b..da3c08eb2 100644 --- a/environment.yml +++ b/environment.yml @@ -3,8 +3,8 @@ channels: - default - conda-forge dependencies: - - python=3.12 - - anaconda=2024.10 + - python=3.13 + - anaconda=2025.06 - pip - pip: - jupyter-book==1.0.3 From 7d40b1e29cc503f523770a8cafc7345da729eaa0 Mon Sep 17 00:00:00 2001 From: mmcky Date: Wed, 30 Jul 2025 12:57:15 +1000 Subject: [PATCH 2/8] update environment --- environment.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/environment.yml b/environment.yml index da3c08eb2..7d28f79d8 100644 --- a/environment.yml +++ b/environment.yml @@ -1,19 +1,19 @@ name: quantecon channels: - default - - conda-forge dependencies: - python=3.13 - anaconda=2025.06 - pip - pip: - - jupyter-book==1.0.3 - - quantecon-book-theme==0.8.2 - - sphinx-tojupyter==0.3.0 + - jupyter-book==1.0.4post1 + - quantecon-book-theme==0.8.3 + - sphinx-tojupyter==0.3.1 - sphinxext-rediraffe==0.2.7 - sphinx-exercise==1.0.1 - - ghp-import==2.1.0 - - sphinxcontrib-youtube==1.3.0 #Version 1.3.0 is required as quantecon-book-theme is only compatible with sphinx<=5 - - sphinx-proof==0.2.0 + - sphinx-proof==0.2.1 + - sphinxcontrib-youtube==1.4.1 - sphinx-togglebutton==0.3.2 - - sphinx-reredirects==0.1.4 #Version 0.1.5 requires sphinx>=7.1 + - sphinx-reredirects==1.0.0 + + From cd0e1aef1fbdae11ef6e436ed1f8db7fb3f244a2 Mon Sep 17 00:00:00 2001 From: mmcky Date: Thu, 31 Jul 2025 11:05:34 +1000 Subject: [PATCH 3/8] adjustments for compat in exercise nodes --- lectures/eigen_I.md | 52 ++++++++------------------------------- lectures/inequality.md | 55 +++++++++++++++--------------------------- lectures/pv.md | 4 --- 3 files changed, 30 insertions(+), 81 deletions(-) diff --git a/lectures/eigen_I.md b/lectures/eigen_I.md index 9cd683526..df0946bed 100644 --- a/lectures/eigen_I.md +++ b/lectures/eigen_I.md @@ -997,12 +997,6 @@ Here is one solution. We start by looking into the distance between the eigenvector approximation and the true eigenvector. ```{code-cell} ipython3 ---- -mystnb: - figure: - caption: Power iteration - name: pow-dist ---- # Define a matrix A A = np.array([[1, 0, 3], [0, 2, 0], @@ -1040,20 +1034,14 @@ print('The real eigenvalue is', np.linalg.eig(A)[0]) plt.figure(figsize=(10, 6)) plt.xlabel('iterations') plt.ylabel('error') +plt.title('Power iteration') _ = plt.plot(errors) ``` -+++ {"user_expressions": []} - Then we can look at the trajectory of the eigenvector approximation. ```{code-cell} ipython3 ---- -mystnb: - figure: - caption: Power iteration trajectory - name: pow-trajectory ---- + # Set up the figure and axis for 3D plot fig = plt.figure() ax = fig.add_subplot(111, projection='3d') @@ -1081,11 +1069,11 @@ ax.legend(points, ['actual eigenvector', r'approximated eigenvector ($b_k$)']) ax.set_box_aspect(aspect=None, zoom=0.8) +ax.set_title('Power iteration trajectory') + plt.show() ``` -+++ {"user_expressions": []} - ```{solution-end} ``` @@ -1119,8 +1107,6 @@ print(f'eigenvectors:\n {eigenvectors}') plot_series(A, v, n) ``` -+++ {"user_expressions": []} - The result seems to converge to the eigenvector of $A$ with the largest eigenvalue. Let's use a [vector field](https://en.wikipedia.org/wiki/Vector_field) to visualize the transformation brought by A. @@ -1128,12 +1114,7 @@ Let's use a [vector field](https://en.wikipedia.org/wiki/Vector_field) to visual (This is a more advanced topic in linear algebra, please step ahead if you are comfortable with the math.) ```{code-cell} ipython3 ---- -mystnb: - figure: - caption: Convergence towards eigenvectors - name: eigen-conv ---- + # Create a grid of points x, y = np.meshgrid(np.linspace(-5, 5, 15), np.linspace(-5, 5, 20)) @@ -1165,13 +1146,12 @@ plt.legend(lines, labels, loc='center left', plt.xlabel("x") plt.ylabel("y") +plt.title("Convergence towards eigenvectors") plt.grid() plt.gca().set_aspect('equal', adjustable='box') plt.show() ``` -+++ {"user_expressions": []} - Note that the vector field converges to the eigenvector of $A$ with the largest eigenvalue and diverges from the eigenvector of $A$ with the smallest eigenvalue. In fact, the eigenvectors are also the directions in which the matrix $A$ stretches or shrinks the space. @@ -1200,12 +1180,7 @@ Use the visualization in the previous exercise to explain the trajectory of the Here is one solution ```{code-cell} ipython3 ---- -mystnb: - figure: - caption: Vector fields of the three matrices - name: vector-field ---- + figure, ax = plt.subplots(1, 3, figsize=(15, 5)) A = np.array([[sqrt(3) + 1, -2], [1, sqrt(3) - 1]]) @@ -1264,11 +1239,10 @@ for i, example in enumerate(examples): ax[i].grid() ax[i].set_aspect('equal', adjustable='box') +plt.title("Vector fields of the three matrices") plt.show() ``` -+++ {"user_expressions": []} - The vector fields explain why we observed the trajectories of the vector $v$ multiplied by $A$ iteratively before. The pattern demonstrated here is because we have complex eigenvalues and eigenvectors. @@ -1276,12 +1250,7 @@ The pattern demonstrated here is because we have complex eigenvalues and eigenve We can plot the complex plane for one of the matrices using `Arrow3D` class retrieved from [stackoverflow](https://stackoverflow.com/questions/22867620/putting-arrowheads-on-vectors-in-a-3d-plot). ```{code-cell} ipython3 ---- -mystnb: - figure: - caption: 3D plot of the vector field - name: 3d-vector-field ---- + class Arrow3D(FancyArrowPatch): def __init__(self, xs, ys, zs, *args, **kwargs): super().__init__((0, 0), (0, 0), *args, **kwargs) @@ -1334,11 +1303,10 @@ ax.set_ylabel('y') ax.set_zlabel('Im') ax.set_box_aspect(aspect=None, zoom=0.8) +plt.title("3D plot of the vector field") plt.draw() plt.show() ``` -+++ {"user_expressions": []} - ```{solution-end} ``` diff --git a/lectures/inequality.md b/lectures/inequality.md index f87645a7b..eb0430dfb 100644 --- a/lectures/inequality.md +++ b/lectures/inequality.md @@ -4,7 +4,7 @@ jupytext: extension: .md format_name: myst format_version: 0.13 - jupytext_version: 1.15.1 + jupytext_version: 1.17.2 kernelspec: display_name: Python 3 (ipykernel) language: python @@ -95,8 +95,6 @@ import wbgapi as wb import plotly.express as px ``` - - ## The Lorenz curve One popular measure of inequality is the Lorenz curve. @@ -239,9 +237,6 @@ ax.legend() plt.show() ``` - - - ### Lorenz curves for US data Next let's look at US data for both income and wealth. @@ -333,7 +328,6 @@ ax.legend() plt.show() ``` - One key finding from this figure is that wealth inequality is more extreme than income inequality. @@ -402,7 +396,7 @@ G = \frac{A}{A+B} $$ where $A$ is the area between the 45-degree line of -perfect equality and the Lorenz curve, while $B$ is the area below the Lorenze curve -- see {numref}`lorenz_gini2`. +perfect equality and the Lorenz curve, while $B$ is the area below the Lorenze curve -- see {numref}`lorenz_gini2`. ```{code-cell} ipython3 --- @@ -427,8 +421,6 @@ ax.legend() plt.show() ``` - - ```{seealso} The World in Data project has a [graphical exploration of the Lorenz curve and the Gini coefficient](https://ourworldindata.org/what-is-the-gini-coefficient) ``` @@ -442,7 +434,6 @@ The code below computes the Gini coefficient from a sample. (code:gini-coefficient)= ```{code-cell} ipython3 - def gini_coefficient(y): r""" Implements the Gini inequality index @@ -503,11 +494,13 @@ for σ in σ_vals: Let's build a function that returns a figure (so that we can use it later in the lecture). ```{code-cell} ipython3 -def plot_inequality_measures(x, y, legend, xlabel, ylabel): +def plot_inequality_measures(x, y, legend, xlabel, ylabel, title=None): fig, ax = plt.subplots() ax.plot(x, y, marker='o', label=legend) ax.set_xlabel(xlabel) ax.set_ylabel(ylabel) + if title is not None: + ax.set_title(title) ax.legend() return fig, ax ``` @@ -546,7 +539,7 @@ We now know the series ID is `SI.POV.GINI`. (Another way to find the series ID is to use the [World Bank data portal](https://data.worldbank.org) and then use `wbgapi` to fetch the data.) -To get a quick overview, let's histogram Gini coefficients across all countries and all years in the World Bank dataset. +To get a quick overview, let's histogram Gini coefficients across all countries and all years in the World Bank dataset. ```{code-cell} ipython3 --- @@ -572,7 +565,7 @@ plt.show() We can see in {numref}`gini_histogram` that across 50 years of data and all countries the measure varies between 20 and 65. -Let us fetch the data `DataFrame` for the USA. +Let us fetch the data `DataFrame` for the USA. ```{code-cell} ipython3 data = wb.data.DataFrame("SI.POV.GINI", "USA") @@ -583,7 +576,6 @@ data.columns = data.columns.map(lambda x: int(x.replace('YR',''))) (This package often returns data with year information contained in the columns. This is not always convenient for simple plotting with pandas so it can be useful to transpose the results before plotting.) - ```{code-cell} ipython3 data = data.T # Obtain years as rows data_usa = data['USA'] # pd.Series of US data @@ -616,8 +608,7 @@ In the previous section we looked at the Gini coefficient for income, focusing o Now let's look at the Gini coefficient for the distribution of wealth. -We will use US data from the {ref}`Survey of Consumer Finances` - +We will use US data from the {ref}`Survey of Consumer Finances` ```{code-cell} ipython3 df_income_wealth.year.describe() @@ -953,9 +944,6 @@ for σ in σ_vals: ```{code-cell} ipython3 --- mystnb: - figure: - caption: Top shares of simulated data - name: top_shares_simulated image: alt: top_shares_simulated --- @@ -963,16 +951,14 @@ fig, ax = plot_inequality_measures(σ_vals, topshares, "simulated data", "$\sigma$", - "top $10\%$ share") + "top $10\%$ share", + "Top shares of simulated data") plt.show() ``` ```{code-cell} ipython3 --- mystnb: - figure: - caption: Gini coefficients of simulated data - name: gini_coef_simulated image: alt: gini_coef_simulated --- @@ -980,16 +966,14 @@ fig, ax = plot_inequality_measures(σ_vals, ginis, "simulated data", "$\sigma$", - "gini coefficient") + "gini coefficient", + "Gini coefficients of simulated data") plt.show() ``` ```{code-cell} ipython3 --- mystnb: - figure: - caption: Lorenz curves for simulated data - name: lorenz_curve_simulated image: alt: lorenz_curve_simulated --- @@ -997,6 +981,7 @@ fig, ax = plt.subplots() ax.plot([0,1],[0,1], label=f"equality") for i in range(len(f_vals)): ax.plot(f_vals[i], l_vals[i], label=f"$\sigma$ = {σ_vals[i]}") +ax.set_title("Lorenz curves for simulated data") plt.legend() plt.show() ``` @@ -1037,9 +1022,6 @@ for f_val, l_val in zip(f_vals_nw, l_vals_nw): ```{code-cell} ipython3 --- mystnb: - figure: - caption: 'US top shares: approximation vs Lorenz' - name: top_shares_us_al image: alt: top_shares_us_al --- @@ -1051,6 +1033,7 @@ ax.plot(years, top_shares_nw, marker='o', label="net wealth-lorenz") ax.set_xlabel("year") ax.set_ylabel("top $10\%$ share") +ax.set_title('US top shares: approximation vs Lorenz') ax.legend() plt.show() ``` @@ -1112,9 +1095,11 @@ def gini(y): g_sum = np.sum(np.abs(y_1 - y_2)) return g_sum / (2 * n * np.sum(y)) ``` + ```{code-cell} ipython3 gini(data.n_wealth.values) ``` + Let's simulate five populations by drawing from a lognormal distribution as before ```{code-cell} ipython3 @@ -1125,6 +1110,7 @@ n = 2_000 μ_vals = -σ_vals**2/2 y_vals = np.exp(μ_vals + σ_vals*np.random.randn(n)) ``` + We can compute the Gini coefficient for these five populations using the vectorized function, the computation time is shown below: ```{code-cell} ipython3 @@ -1133,14 +1119,13 @@ gini_coefficients =[] for i in range(k): gini_coefficients.append(gini(y_vals[i])) ``` + This shows the vectorized function is much faster. This gives us the Gini coefficients for these five households. ```{code-cell} ipython3 gini_coefficients ``` -```{solution-end} -``` - - +```{solution-end} +``` \ No newline at end of file diff --git a/lectures/pv.md b/lectures/pv.md index da7d925d0..1a10dbc21 100644 --- a/lectures/pv.md +++ b/lectures/pv.md @@ -339,8 +339,6 @@ $$ (eq:Ainv) Check this by showing that $A A^{-1}$ is equal to the identity matrix. - - ```{exercise-end} ``` @@ -473,8 +471,6 @@ following settings for $d$ and $p_{T+1}^*$: Plugging each of the above $p_{T+1}^*, d_t$ pairs into Equation {eq}`eq:ptpveq` yields: 1. $ p_t = \sum^T_{s=t} \delta^{s-t} g^s d_0 = d_t \frac{1 - (\delta g)^{T+1-t}}{1 - \delta g}$ - - 2. $p_t = \sum^T_{s=t} \delta^{s-t} g^s d_0 + \frac{\delta^{T+1-t} g^{T+1} d_0}{1 - \delta g} = \frac{d_t}{1 - \delta g}$ 3. $p_t = 0$ 4. $p_t = c \delta^{-t}$ From 34999cbf882efba0cd555c900d993d1dd18db076 Mon Sep 17 00:00:00 2001 From: mmcky Date: Thu, 31 Jul 2025 12:30:56 +1000 Subject: [PATCH 4/8] adjust title on eigen_I.md --- lectures/eigen_I.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lectures/eigen_I.md b/lectures/eigen_I.md index df0946bed..270451a73 100644 --- a/lectures/eigen_I.md +++ b/lectures/eigen_I.md @@ -1239,7 +1239,7 @@ for i, example in enumerate(examples): ax[i].grid() ax[i].set_aspect('equal', adjustable='box') -plt.title("Vector fields of the three matrices") +fig.suptitle("Vector fields of the three matrices") plt.show() ``` From f26b5cf7cab8d088a611a9a10f1fc061bb5c309f Mon Sep 17 00:00:00 2001 From: mmcky Date: Thu, 31 Jul 2025 12:45:20 +1000 Subject: [PATCH 5/8] adjust fig --- lectures/eigen_I.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lectures/eigen_I.md b/lectures/eigen_I.md index 270451a73..4f6790171 100644 --- a/lectures/eigen_I.md +++ b/lectures/eigen_I.md @@ -1181,7 +1181,7 @@ Here is one solution ```{code-cell} ipython3 -figure, ax = plt.subplots(1, 3, figsize=(15, 5)) +fig, ax = plt.subplots(1, 3, figsize=(15, 5)) A = np.array([[sqrt(3) + 1, -2], [1, sqrt(3) - 1]]) A = (1/(2*sqrt(2))) * A From 8014cbe422e4ca8d58a16aa9974847b592ede417 Mon Sep 17 00:00:00 2001 From: mmcky Date: Fri, 1 Aug 2025 11:49:11 +1000 Subject: [PATCH 6/8] tmp: disable build cache --- .github/workflows/ci.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 34c95d46e..fd6dfc567 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,13 +37,13 @@ jobs: - name: Display Pip Versions shell: bash -l {0} run: pip list - - name: Download "build" folder (cache) - uses: dawidd6/action-download-artifact@v11 - with: - workflow: cache.yml - branch: main - name: build-cache - path: _build + # - name: Download "build" folder (cache) + # uses: dawidd6/action-download-artifact@v11 + # with: + # workflow: cache.yml + # branch: main + # name: build-cache + # path: _build # Build Assets (Download Notebooks and PDF via LaTeX) - name: Build PDF from LaTeX shell: bash -l {0} From 75f30440e61198f2b226a8203da79881a1b2fcb2 Mon Sep 17 00:00:00 2001 From: mmcky Date: Fri, 1 Aug 2025 14:06:13 +1000 Subject: [PATCH 7/8] re-enable build cache --- .github/workflows/ci.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fd6dfc567..34c95d46e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,13 +37,13 @@ jobs: - name: Display Pip Versions shell: bash -l {0} run: pip list - # - name: Download "build" folder (cache) - # uses: dawidd6/action-download-artifact@v11 - # with: - # workflow: cache.yml - # branch: main - # name: build-cache - # path: _build + - name: Download "build" folder (cache) + uses: dawidd6/action-download-artifact@v11 + with: + workflow: cache.yml + branch: main + name: build-cache + path: _build # Build Assets (Download Notebooks and PDF via LaTeX) - name: Build PDF from LaTeX shell: bash -l {0} From a7f675ae7b84283a9c55a330f1d7475e383f2ba2 Mon Sep 17 00:00:00 2001 From: mmcky Date: Fri, 1 Aug 2025 14:06:57 +1000 Subject: [PATCH 8/8] update cache bevaiour to schedule or manual build --- .github/workflows/cache.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/cache.yml b/.github/workflows/cache.yml index 6d6853f4e..19784bca4 100644 --- a/.github/workflows/cache.yml +++ b/.github/workflows/cache.yml @@ -1,8 +1,9 @@ name: Build Cache [using jupyter-book] on: - push: - branches: - - main + schedule: + # Execute cache weekly at 3am on Monday + - cron: '0 3 * * 1' + workflow_dispatch: jobs: tests: runs-on: ubuntu-latest