41. A Lake Model of Employment#
41.1. Outline#
In addition to what’s in Anaconda, this lecture will need the following libraries:
import numpy as np
import matplotlib.pyplot as plt
41.2. The Lake model#
This model is sometimes called the lake model because there are two pools of workers:
those who are currently employed.
those who are currently unemployed but are seeking employment.
The “flows” between the two lakes are as follows:
workers exit the labor market at rate
 .new workers enter the labor market at rate
 .employed workers separate from their jobs at rate
 .unemployed workers find jobs at rate
 .
The graph below illustrates the lake model.
Fig. 41.1 An illustration of the lake model#
41.3. Dynamics#
Let 
The total population of workers is 
The number of unemployed and employed workers thus evolves according to:
We can arrange (41.1) as a linear system of equations in matrix form 
Suppose at 
Then, 
Thus the long-run outcomes of this system may depend on the initial condition 
We are interested in how 
What long-run unemployment rate and employment rate should we expect?
Do long-run outcomes depend on the initial values 
41.3.1. Visualising the long-run outcomes#
Let us first plot the time series of unemployment 
class LakeModel:
    """
    Solves the lake model and computes dynamics of the unemployment stocks and
    rates.
    Parameters:
    ------------
    λ : scalar
        The job finding rate for currently unemployed workers
    α : scalar
        The dismissal rate for currently employed workers
    b : scalar
        Entry rate into the labor force
    d : scalar
        Exit rate from the labor force
    """
    def __init__(self, λ=0.1, α=0.013, b=0.0124, d=0.00822):
        self.λ, self.α, self.b, self.d = λ, α, b, d
        λ, α, b, d = self.λ, self.α, self.b, self.d
        self.g = b - d
        g = self.g
        self.A = np.array([[(1-d)*(1-λ) + b,   α*(1-d) + b],
                           [        (1-d)*λ,   (1-α)*(1-d)]])
        self.ū = (1 + g - (1 - d) * (1 - α)) / (1 + g - (1 - d) * (1 - α) + (1 - d) * λ)
        self.ē = 1 - self.ū
    def simulate_path(self, x0, T=1000):
        """
        Simulates the sequence of employment and unemployment
        Parameters
        ----------
        x0 : array
            Contains initial values (u0,e0)
        T : int
            Number of periods to simulate
        Returns
        ----------
        x : iterator
            Contains sequence of employment and unemployment rates
        """
        x0 = np.atleast_1d(x0)  # Recast as array just in case
        x_ts= np.zeros((2, T))
        x_ts[:, 0] = x0
        for t in range(1, T):
            x_ts[:, t] = self.A @ x_ts[:, t-1]
        return x_ts
lm = LakeModel()
e_0 = 0.92          # Initial employment
u_0 = 1 - e_0       # Initial unemployment, given initial n_0 = 1
lm = LakeModel()
T = 100         # Simulation length
x_0 = (u_0, e_0)
x_path = lm.simulate_path(x_0, T)
fig, axes = plt.subplots(3, 1, figsize=(10, 8))
axes[0].plot(x_path[0, :], lw=2)
axes[0].set_title('Unemployment')
axes[1].plot(x_path[1, :], lw=2)
axes[1].set_title('Employment')
axes[2].plot(x_path.sum(0), lw=2)
axes[2].set_title('Labor force')
for ax in axes:
    ax.grid()
plt.tight_layout()
plt.show()
Not surprisingly, we observe that labor force 
This coincides with the fact there is only one inflow source (new entrants pool) to unemployment and employment pools.
The inflow and outflow of labor market system is determined by constant exit rate and entry rate of labor market in the long run.
In detail, let 
Observe that
Hence, the growth rate of 
Moreover, the times series of unemployment and employment seems to grow at some stable rates in the long run.
41.3.2. The application of Perron-Frobenius theorem#
Since by intuition if we consider unemployment pool and employment pool as a closed system, the growth should be similar to the labor force.
We next ask whether the long-run growth rates of 
The answer will be clearer if we appeal to Perron-Frobenius theorem.
The importance of the Perron-Frobenius theorem stems from the fact that firstly in the real world most matrices we encounter are nonnegative matrices.
Secondly, many important models are simply linear iterative models that
begin with an initial condition 
This theorem helps characterise the dominant eigenvalue 
41.3.2.1. Dominant eigenvector#
We now illustrate the power of the Perron-Frobenius theorem by showing how it helps us to analyze the lake model.
Since 
the spectral radius
 is an eigenvalue of , where
any other eigenvalue
 in absolute value is strictly smaller than : ,there exist unique and everywhere positive right eigenvector
 (column vector) and left eigenvector (row vector):
if further
 is positive, then with we have
The last statement implies that the magnitude of 
Therefore, the magnitude 
Recall that the spectral radius is bounded by column sums: for 
Note that 
Denote 
The Perron-Frobenius implies that there is a unique positive eigenvector 
Since 
This dominant eigenvector plays an important role in determining long-run outcomes as illustrated below.
def plot_time_paths(lm, x0=None, T=1000, ax=None):
        """
        Plots the simulated time series.
        Parameters
        ----------
        lm : class
            Lake Model
        x0 : array
            Contains some different initial values.
        T : int
            Number of periods to simulate
        """
        if x0 is None:
            x0 = np.array([[5.0, 0.1]])
        ū, ē = lm.ū, lm.ē
        x0 = np.atleast_2d(x0)
        if ax is None:
            fig, ax = plt.subplots(figsize=(10, 8))
            # Plot line D
            s = 10
            ax.plot([0, s * ū], [0, s * ē], "k--", lw=1, label='set $D$')
        # 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_xlim(-2, 6)
        ax.set_ylim(-2, 6)
        ax.set_xlabel("unemployed workforce")
        ax.set_ylabel("employed workforce")
        ax.set_xticks((0, 6))
        ax.set_yticks((0, 6))
        # Plot time series
        for x in x0:
            x_ts = lm.simulate_path(x0=x)
            ax.scatter(x_ts[0, :], x_ts[1, :], s=4,)
            u0, e0 = x
            ax.plot([u0], [e0], "ko", ms=2, alpha=0.6)
            ax.annotate(f'$x_0 = ({u0},{e0})$',
                        xy=(u0, e0),
                        xycoords="data",
                        xytext=(0, 20),
                        textcoords="offset points",
                        arrowprops=dict(arrowstyle = "->"))
        ax.plot([ū], [ē], "ko", ms=4, alpha=0.6)
        ax.annotate(r'$\bar{x}$',
                xy=(ū, ē),
                xycoords="data",
                xytext=(20, -20),
                textcoords="offset points",
                arrowprops=dict(arrowstyle = "->"))
        if ax is None:
            plt.show()
lm = LakeModel(α=0.01, λ=0.1, d=0.02, b=0.025)
x0 = ((5.0, 0.1), (0.1, 4.0), (2.0, 1.0))
plot_time_paths(lm, x0=x0)
Since 
This set 
The graph illustrates that for two distinct initial conditions 
This suggests that all such sequences share strong similarities in the long run, determined by the dominant eigenvector 
41.3.2.2. Negative growth rate#
In the example illustrated above we considered parameters such that overall growth rate of the labor force 
Suppose now we are faced with a situation where the 
This means that 
What would the behavior of the iterative sequence 
This is visualised below.
Thus, while the sequence of iterates still moves towards the dominant eigenvector 
This is a result of the fact that 
This leads us to the next result.
41.3.3. Properties#
Since the column sums of 
Perron-Frobenius theory implies that
As a result, for any 
as 
We see that the growth of 
Moreover, the long-run unemployment and employment are steady fractions of 
The latter implies that 
In detail, we have the unemployment rates and employment rates: 
To illustrate the dynamics of the rates, let 
The dynamics of the rates follow
Observe that the column sums of 
One can check that 
Moreover, 
This is illustrated below.
lm = LakeModel()
e_0 = 0.92          # Initial employment
u_0 = 1 - e_0       # Initial unemployment, given initial n_0 = 1
lm = LakeModel()
T = 100         # Simulation length
x_0 = (u_0, e_0)
x_path = lm.simulate_path(x_0, T)
rate_path = x_path / x_path.sum(0)
fig, axes = plt.subplots(2, 1, figsize=(10, 8))
# Plot steady ū and ē
axes[0].hlines(lm.ū, 0, T, 'r', '--', lw=2, label='ū')
axes[1].hlines(lm.ē, 0, T, 'r', '--', lw=2, label='ē')
titles = ['Unemployment rate', 'Employment rate']
locations = ['lower right', 'upper right']
# Plot unemployment rate and employment rate
for i, ax in enumerate(axes):
    ax.plot(rate_path[i, :], lw=2, alpha=0.6)
    ax.set_title(titles[i])
    ax.grid()
    ax.legend(loc=locations[i])
plt.tight_layout()
plt.show()
To provide more intuition for convergence, we further explain the convergence below without the Perron-Frobenius theorem.
Suppose that 
Let 
The dynamics of the rates follow 
Consider 
Then, we have 
Hence, we obtain 
Since 
Therefore, the convergence follows 
Since the column sums of 
In this case, 
41.4. Exercise#
Exercise 41.1 (Evolution of unemployment and employment rate)
How do the long-run unemployment rate and employment rate evolve if there is an increase in the separation rate 
Is the result compatible with your intuition?
Plot the graph to illustrate how the line 
Solution to Exercise 41.1 (Evolution of unemployment and employment rate)
Eq. (41.3) implies that the long-run unemployment rate will increase, and the employment rate will decrease
if 
Suppose first that 
The below graph illustrates that the line 
fig, ax = plt.subplots(figsize=(10, 8))
lm = LakeModel(α=0.01, λ=0.1, d=0.02, b=0.025)
plot_time_paths(lm, ax=ax)
s=10
ax.plot([0, s * lm.ū], [0, s * lm.ē], "k--", lw=1, label='set $D$, α=0.01')
lm = LakeModel(α=0.04, λ=0.1, d=0.02, b=0.025)
plot_time_paths(lm, ax=ax)
ax.plot([0, s * lm.ū], [0, s * lm.ē], "r--", lw=1, label='set $D$, α=0.04')
ax.legend(loc='best')
plt.show()




