Chapter 2 Code#

We’ll use Python’s NumPy library in what follows, as well as Matplotlib for plotting.

import numpy as np
import matplotlib.pyplot as plt

Bisection#

Here’s an implementation of the bisection algorithm. We will test it on this function:

f = lambda x: np.sin(4 * (x - 1/4)) + x + x**20 - 1

The implementation:

M = 1000
ϵ = 1e-8
α, β = 0, 1

    
i = 1
a, b = α, β
while i <= M:
    c = (a + b) / 2
    if abs(f(c)) < ϵ:
        print(c)
        break
    i += 1
    if f(a) * f(c) < 0:
        b = c
    else:
        a = c
        
if i > M:
    print("Failed to converge.")
0.408293504267931

User Defined Functions#

Here’s the basic implementation of the function \(\tau\).

def tau(z, S, phi):
    """
    Evaluates the function tau(z) given data S, phi, where
    S and phi are assumed to be arrays.
    """
    a = 0
    for i, x in enumerate(S):
        b = a + phi[i]
        if a < z <= b:
            return x
        a = b

Here’s a more efficient implementation.

def tau(z, S, phi):
    i = np.searchsorted(np.cumsum(phi), z)
    return S[i]

And here’s a closure that generates the function \(\tau\).

def tau_factory(S, phi):
    Φ = np.cumsum(phi)
    
    def tau(z):
        i = np.searchsorted(Φ, z)
        return S[i]
    
    return tau

We generate a function \(\tau\) that acts on \(z\) alone by calling the function factory:

phi = 0.2, 0.5, 0.3
S = 0, 1, 2
tau = tau_factory(S, phi)
tau(0.1)  # Should be 0
0

All of these functions work as expected. To illustrate, here \(\tau\) is used to generate draws from a given distribution \(\phi\).

size = 100_000

draws = np.empty(size)
for j in range(size):
    W = np.random.uniform()
    draws[j] = tau(W)
    
# Compute fraction of draws with each possible value
frequency = [np.mean(draws==j) for j in S]
    

Let’s check that the empirical frequency approximately coincides with the probabilities in \(\phi\).

fig, ax = plt.subplots()

ax.bar(S, frequency, 
       edgecolor='k',
       facecolor='b',
       alpha=0.25, 
       label="histogram")

ax.stem(S, phi, label='$\\phi$')

ax.legend()

plt.show()
_images/baa5b94c4d62685363a86ed9207dab668f7050ba7ae157fa14828ad9c31da617.png

Object Oriented Programming#

Here’s a class that implements the function \(\tau\) as a method, as well as a method to generate draws from \(\phi\).

class Tau:
    
    def __init__(self, S, phi):
        self.S = S
        self.Φ = np.cumsum(phi)
    
    def tau(self, z):
        i = np.searchsorted(self.Φ, z)
        return self.S[i]
    
    def draw(self):
        W = np.random.uniform()
        return self.tau(W)
tau = Tau(S, phi)
for i in range(5):
    print(tau.draw())
0
2
0
0
1