Appendix Code#

We begin with some imports

import numpy as np                                            
import matplotlib.pyplot as plt       
import quantecon_book_networks                  
from mpl_toolkits.mplot3d.axes3d import Axes3D, proj3d  
from matplotlib import cm          
export_figures = False
quantecon_book_networks.config("matplotlib")                     
---------------------------------------------------------------------------
ModuleNotFoundError                       Traceback (most recent call last)
Cell In[1], line 3
      1 import numpy as np                                            
      2 import matplotlib.pyplot as plt       
----> 3 import quantecon_book_networks                  
      4 from mpl_toolkits.mplot3d.axes3d import Axes3D, proj3d  
      5 from matplotlib import cm          

ModuleNotFoundError: No module named 'quantecon_book_networks'

Functions#

One-to-one and onto functions on \((0,1)\)#

We start by defining the domain and our one-to-one and onto function examples.

x = np.linspace(0, 1, 100)
titles = 'one-to-one', 'onto'
labels = '$f(x)=1/2 + x/2$', '$f(x)=4x(1-x)$'
funcs = lambda x: 1/2 + x/2, lambda x: 4 * x * (1-x)

The figure can now be produced as follows.

fig, axes = plt.subplots(1, 2, figsize=(10, 4))
for f, ax, lb, ti in zip(funcs, axes, labels, titles):
    ax.set_xlim(0, 1)
    ax.set_ylim(0, 1.01)
    ax.plot(x, f(x), label=lb)
    ax.set_title(ti, fontsize=12)
    ax.legend(loc='lower center', fontsize=12, frameon=False)
    ax.set_xticks((0, 1))
    ax.set_yticks((0, 1))

if export_figures:
    plt.savefig("figures/func_types_1.pdf")
plt.show()

Some functions are bijections#

This figure can be produced in a similar manner to 6.1.

x = np.linspace(0, 1, 100)
titles = 'constant', 'bijection'
labels = '$f(x)=1/2$', '$f(x)=1-x$'
funcs = lambda x: 1/2 + 0 * x, lambda x: 1-x

fig, axes = plt.subplots(1, 2, figsize=(10, 4))
for f, ax, lb, ti in zip(funcs, axes, labels, titles):
    ax.set_xlim(0, 1)
    ax.set_ylim(0, 1.01)
    ax.plot(x, f(x), label=lb)
    ax.set_title(ti, fontsize=12)
    ax.legend(loc='lower center', fontsize=12, frameon=False)
    ax.set_xticks((0, 1))
    ax.set_yticks((0, 1))
    
if export_figures:
    plt.savefig("figures/func_types_2.pdf")
plt.show()

Fixed Points#

Graph and fixed points of \(G \colon x \mapsto 2.125/(1 + x^{-4})\)#

We begin by defining the domain and the function.

xmin, xmax = 0.0000001, 2
xgrid = np.linspace(xmin, xmax, 200)
g = lambda x: 2.125 / (1 + x**(-4))

Next we define our fixed points.

fps_labels = ('$x_\ell$', '$x_m$', '$x_h$' )
fps = (0.01, 0.94, 1.98)
coords = ((40, 80), (40, -40), (-40, -80))

Finally we can produce the figure.

fig, ax = plt.subplots(figsize=(6.5, 6))

ax.set_xlim(xmin, xmax)
ax.set_ylim(xmin, xmax)

ax.plot(xgrid, g(xgrid), 'b-', lw=2, alpha=0.6, label='$G$')
ax.plot(xgrid, xgrid, 'k-', lw=1, alpha=0.7, label='$45^o$')

ax.legend(fontsize=14)

ax.plot(fps, fps, 'ro', ms=8, alpha=0.6)

for (fp, lb, coord) in zip(fps, fps_labels, coords):
    ax.annotate(lb, 
             xy=(fp, fp),
             xycoords='data',
             xytext=coord,
             textcoords='offset points',
             fontsize=16,
             arrowprops=dict(arrowstyle="->"))

if export_figures:
    plt.savefig("figures/three_fixed_points.pdf")
plt.show()

Complex Numbers#

The complex number \((a, b) = r e^{i \phi}\)#

We start by abbreviating some useful values and functions.

π = np.pi
zeros = np.zeros
ones = np.ones
fs = 18

Next we set our parameters.

r = 2
φ = π/3
x = r * np.cos(φ)
x_range = np.linspace(0, x, 1000)
φ_range = np.linspace(0, φ, 1000)

Finally we produce the plot.

fig = plt.figure(figsize=(7, 7))
ax = plt.subplot(111, projection='polar')

ax.plot((0, φ), (0, r), marker='o', color='b', alpha=0.5)          # Plot r
ax.plot(zeros(x_range.shape), x_range, color='b', alpha=0.5)       # Plot x
ax.plot(φ_range, x / np.cos(φ_range), color='b', alpha=0.5)        # Plot y
ax.plot(φ_range, ones(φ_range.shape) * 0.15, color='green')  # Plot φ

ax.margins(0) # Let the plot starts at origin

ax.set_rmax(2)
ax.set_rticks((1, 2))  # Less radial ticks
ax.set_rlabel_position(-88.5)    # Get radial labels away from plotted line

ax.text(φ, r+0.04 , r'$(a, b) = (1, \sqrt{3})$', fontsize=fs)   # Label z
ax.text(φ+0.4, 1 , '$r = 2$', fontsize=fs)                             # Label r
ax.text(0-0.4, 0.5, '$1$', fontsize=fs)                            # Label x
ax.text(0.5, 1.2, '$\sqrt{3}$', fontsize=fs)                      # Label y
ax.text(0.3, 0.25, '$\\varphi = \\pi/3$', fontsize=fs)                   # Label θ

xT=plt.xticks()[0]
xL=['0',
    r'$\frac{\pi}{4}$',
    r'$\frac{\pi}{2}$',
    r'$\frac{3\pi}{4}$',
    r'$\pi$',
    r'$\frac{5\pi}{4}$',
    r'$\frac{3\pi}{2}$',
    r'$\frac{7\pi}{4}$']

plt.xticks(xT, xL, fontsize=fs+2)
ax.grid(True)

if export_figures:
    plt.savefig("figures/complex_number.pdf")
plt.show()

Convergence#

Convergence of a sequence to the origin in \(\mathbb{R}^3\)#

We define our transformation matrix, initial point, and number of iterations.

θ = 0.1
A = ((np.cos(θ), - np.sin(θ), 0.0001),
     (np.sin(θ),   np.cos(θ), 0.001),
     (np.sin(θ),   np.cos(θ), 1))

A = 0.98 * np.array(A)
p = np.array((1, 1, 1))
n = 200

Now we can produce the plot by repeatedly transforming our point with the transformation matrix and plotting each resulting point.

fig = plt.figure(figsize=(6, 6))
ax = fig.add_subplot(111, projection='3d')
ax.view_init(elev=20, azim=-40)

ax.set_xlim((-1.5, 1.5))
ax.set_ylim((-1.5, 1.5))
ax.set_xticks((-1,0,1))
ax.set_yticks((-1,0,1))

for i in range(n):
    x, y, z = p
    ax.plot([x], [y], [z], 'o', ms=4, color=cm.jet_r(i / n))
    p = A @ p
    
if export_figures:
    plt.savefig("figures/euclidean_convergence_1.pdf")
plt.show()

Linear Algebra#

The span of vectors \(u\), \(v\), \(w\) in \(\mathbb{R}\)#

We begin by importing the FancyArrowPatch class and extending it.

from matplotlib.patches import FancyArrowPatch

class Arrow3D(FancyArrowPatch):
    def __init__(self, xs, ys, zs, *args, **kwargs):
        FancyArrowPatch.__init__(self, (0,0), (0,0), *args, **kwargs)
        self._verts3d = xs, ys, zs

    def do_3d_projection(self, renderer=None):
        xs3d, ys3d, zs3d = self._verts3d
        xs, ys, zs = proj3d.proj_transform(xs3d, ys3d, zs3d, self.axes.M)
        self.set_positions((xs[0],ys[0]),(xs[1],ys[1]))

        return np.min(zs)

    def draw(self, renderer):
        xs3d, ys3d, zs3d = self._verts3d
        xs, ys, zs = proj3d.proj_transform(xs3d, ys3d, zs3d, self.axes.M)
        self.set_positions((xs[0],ys[0]),(xs[1],ys[1]))
        FancyArrowPatch.draw(self, renderer)

Next we generate our vectors \(u\), \(v\), \(w\), ensuring linear dependence.

α, β = 0.2, 0.1
def f(x, y):
    return α * x + β * y

# Vector locations, by coordinate
x_coords = np.array((3, 3, -3.5))
y_coords = np.array((4, -4, 3.0))
z_coords = f(x_coords, y_coords)

vecs = [np.array((x, y, z)) for x, y, z in zip(x_coords, y_coords, z_coords)]

Next we define the spanning plane.

x_min, x_max = -5, 5
y_min, y_max = -5, 5

grid_size = 20
xr2 = np.linspace(x_min, x_max, grid_size)
yr2 = np.linspace(y_min, y_max, grid_size)
x2, y2 = np.meshgrid(xr2, yr2)
z2 = f(x2, y2)

Finally we generate the plot.

fig = plt.figure(figsize=(12, 7))
ax = plt.axes(projection ='3d')
ax.view_init(elev=10., azim=-80)

ax.set(xlim=(x_min, x_max), 
       ylim=(x_min, x_max), 
       zlim=(x_min, x_max),
       xticks=(0,), yticks=(0,), zticks=(0,))

# Draw the axes
gs = 3
z = np.linspace(x_min, x_max, gs)
x = np.zeros(gs)
y = np.zeros(gs)
ax.plot(x, y, z, 'k-', lw=2, alpha=0.5)
ax.plot(z, x, y, 'k-', lw=2, alpha=0.5)
ax.plot(y, z, x, 'k-', lw=2, alpha=0.5)

# Draw the vectors
for v in vecs:
    a = Arrow3D([0, v[0]], 
                [0, v[1]], 
                [0, v[2]], 
                mutation_scale=20, 
                lw=1, 
                arrowstyle="-|>", 
                color="b")
    ax.add_artist(a)


for v, label in zip(vecs, ('u', 'v', 'w')):
    v = v * 1.1
    ax.text(v[0], v[1], v[2], 
            f'${label}$', 
            fontsize=14)

# Draw the plane
grid_size = 20
xr2 = np.linspace(x_min, x_max, grid_size)
yr2 = np.linspace(y_min, y_max, grid_size)
x2, y2 = np.meshgrid(xr2, yr2)
z2 = f(x2, y2)

ax.plot_surface(x2, y2, z2, rstride=1, cstride=1, cmap=cm.jet,
                linewidth=0, antialiased=True, alpha=0.2)


if export_figures:
    plt.savefig("figures/span1.pdf")
plt.show()

Linear Maps Are Matrices#

Equivalence of the onto and one-to-one properties (for linear maps)#

This plot is produced similarly to Figures 6.1 and 6.2.

fig, axes = plt.subplots(1, 2, figsize=(10, 4))

x = np.linspace(-2, 2, 10)

titles = 'non-bijection', 'bijection'
labels = '$f(x)=0 x$', '$f(x)=0.5 x$'
funcs = lambda x: 0*x, lambda x: 0.5 * x

for ax, f, lb, ti in zip(axes, funcs, labels, titles):

    # Set the axes through the origin
    for spine in ['left', 'bottom']:
        ax.spines[spine].set_position('zero')
    for spine in ['right', 'top']:
        ax.spines[spine].set_color('none')
    ax.set_yticks((-1,  1))
    ax.set_ylim((-1, 1))
    ax.set_xlim((-1, 1))
    ax.set_xticks((-1, 1))
    y = f(x)
    ax.plot(x, y, '-', linewidth=4, label=lb, alpha=0.6)
    ax.text(-0.8, 0.5, ti, fontsize=14)
    ax.legend(loc='lower right', fontsize=12)
    
if export_figures:
    plt.savefig("figures/func_types_3.pdf")
plt.show()

Convexity and Polyhedra#

A polyhedron \(P\) represented as intersecting halfspaces#

Inequalities are of the form

\[ a x + b y \leq c \]

To plot the halfspace we plot the line

\[ y = c/b - a/b x \]

and then fill in the halfspace using fill_between on points \(x, y, \hat y\), where \(\hat y\) is either y_min or y_max.

fig, ax = plt.subplots()
plt.axis('off')

x = np.linspace(-10, 14, 200)
y_min, y_max = -2, 3

a1, b1, c1 = 1.0, 8.0, -5.0
y = c1 / b1 - (a1 / b1) * x
ax.plot(x, y, label='$a_1 x_1 + b_1 x_2 = c_1$')
ax.fill_between(x, y, y_max, alpha=0.2)


a2, b2, c2 = 0.5, 0.75, 1.5
y = c2 / b2 - (a2 / b2) * x
ax.plot(x, y, label='$a_2 x_1 + b_2 x_2 = c_2$')
ax.fill_between(x, y, y_min, alpha=0.2)


a3, b3, c3 = -1.0, 1.0, 1.5
y = c3 / b3 - (a3 / b3) * x
ax.plot(x, y, label='$a_3 x_1 + b_3 x_2 = c_3$')
ax.fill_between(x, y, y_min, alpha=0.2)

ax.plot((0.23,), (1.82,), 'ko')
ax.plot((-1.95,), (-0.4,), 'ko')
ax.plot((4.8,), (-1.2,), 'ko')


ax.annotate('$P$', xy=(0, 0), fontsize=12)

ax.set_ylim(y_min, y_max)
if export_figures:
    plt.savefig("figures/polyhedron1.pdf")
plt.show()

Saddle Points and Duality#

fig = plt.figure(figsize=(8.5, 6))

## Top left Plot

ax = fig.add_subplot(221, projection='3d')

plot_args = {'rstride': 1, 'cstride': 1, 'cmap':"viridis",
             'linewidth': 0.4, 'antialiased': True, "alpha":0.75,
             'vmin': -1, 'vmax': 1}

x, y = np.mgrid[-1:1:31j, -1:1:31j]
z = x**2 - y**2

ax.plot_surface(x, y, z, **plot_args)

ax.view_init(azim=245, elev=20)

ax.set_xlim(-1, 1)
ax.set_ylim(-1, 1)
ax.set_zlim(-1, 1)

ax.set_xticks([0])
ax.set_xticklabels([r"$x^*$"], fontsize=16)
ax.set_yticks([0])
ax.set_yticklabels([r"$\theta^*$"], fontsize=16)


ax.set_zticks([])
ax.set_zticklabels([])

ax.w_xaxis.set_pane_color((1.0, 1.0, 1.0, 0.0)) 
ax.w_yaxis.set_pane_color((1.0, 1.0, 1.0, 0.0)) 
ax.w_zaxis.set_pane_color((1.0, 1.0, 1.0, 0.0))

ax.set_zlabel("$L(x,\\theta)$", fontsize=14)

## Top Right Plot

ax = fig.add_subplot(222)


plot_args = {'cmap':"viridis", 'antialiased': True, "alpha":0.6,
             'vmin': -1, 'vmax': 1}

x, y = np.mgrid[-1:1:31j, -1:1:31j]
z = x**2 - y**2

ax.contourf(x, y, z, **plot_args)

ax.plot([0], [0], 'ko')

ax.set_xlim(-1, 1)
ax.set_ylim(-1, 1)

plt.xticks([ 0],
           [r"$x^*$"], fontsize=16)

plt.yticks([0],
           [r"$\theta^*$"], fontsize=16)

ax.hlines(0, -1, 1, color='k', ls='-', lw=1)
ax.vlines(0, -1, 1, color='k', ls='-', lw=1)

coords=(-35, 30)
ax.annotate(r'$L(x, \theta^*)$', 
             xy=(-0.5, 0),  
             xycoords="data",
             xytext=coords,
             textcoords="offset points",
             fontsize=12,
             arrowprops={"arrowstyle" : "->"})

coords=(35, 30)
ax.annotate(r'$L(x^*, \theta)$', 
             xy=(0, 0.25),  
             xycoords="data",
             xytext=coords,
             textcoords="offset points",
             fontsize=12,
             arrowprops={"arrowstyle" : "->"})

## Bottom Left Plot

ax = fig.add_subplot(223)

x = np.linspace(-1, 1, 100)
ax.plot(x, -x**2, label='$\\theta \mapsto L(x^*, \\theta)$')
ax.set_ylim((-1, 1))
ax.legend(fontsize=14)
ax.set_xticks([])
ax.set_yticks([])

## Bottom Right Plot

ax = fig.add_subplot(224)

x = np.linspace(-1, 1, 100)
ax.plot(x, x**2, label='$x \mapsto L(x, \\theta^*)$')
ax.set_ylim((-1, 1))
ax.legend(fontsize=14, loc='lower right')
ax.set_xticks([])
ax.set_yticks([])

if export_figures:
	plt.savefig("figures/saddle_1.pdf")
plt.show()