GitHub

@@ -134,7 +134,7 @@ Let

134134

Here **maximum lifetime value** means the value of {eq}`objective` when

135135

the worker makes optimal decisions at all future points in time.

136136137-

As we now show, these obtaining these functions is key to solving the new model.

137+

As we now show, obtaining these functions is key to solving the model.

138138139139

### The Bellman Equations

140140

@@ -199,27 +199,211 @@ enough information to solve for both $v_e$ and $v_u$.

199199200200

Once we have them in hand, we will be able to make optimal choices.

201201202-

(ast_mcm)=

203-

### A Simplifying Transformation

204202205-

Rather than jumping straight into solving these equations, let's see if we can

206-

simplify them somewhat.

203+

### The Reservation Wage

207204208-

(This process will be analogous to our {ref}`second pass <mm_op2>` at the plain vanilla

209-

McCall model, where we reduced the Bellman equation to an equation in an unknown

210-

scalar value, rather than an unknown vector.)

211205212-

First, let

206+

Let

213207214208

```{math}

215209

:label: defh_mm

216210217-

h := u(c) + \beta \sum_{w' \in \mathbb W} v_u(w') q(w')

211+

h := u(c) + \beta \sum_{w' \in \mathbb W} v_u(w') q(w')

218212

```

219213220-

be the continuation value associated with unemployment (the value of rejecting the current offer).

214+

This is the continuation value for an unemployed agent (the value of rejecting the current offer).

215+216+

If we know $v_u$ then we can easily compute $h$.

221217222-

We can now write {eq}`bell2_mccall` as

218+

From {eq}`bell2_mccall`, we see that an unemployed agent accepts current offer $w$ if $v_e(w) \geq h$.

219+220+

This means precisely that the value of accepting is higher than the value of rejecting.

221+222+

It is clear that $v_e$ is (at least weakly) increasing in $w$, since the agent is never made worse off by a higher wage offer.

223+224+

Hence, we can express the optimal choice as accepting wage offer $w$ if and only if $w \geq \bar w$,

225+

where the **reservation wage** $\bar w$ is the first wage level $w$ such that

226+227+

$$

228+

v_e(w) \geq h

229+

$$

230+231+232+233+

## Code

234+235+

Let's now implement a solution method based on the two Bellman equations.

236+237+

### Set Up

238+239+

In the code, you'll see that we use a class to store the various parameters and other

240+

objects associated with a given model.

241+242+

This helps to tidy up the code and provides an object that's easy to pass to functions.

243+244+

The default utility function is a CRRA utility function

245+246+

```{code-cell} ipython3

247+

def u(c, γ):

248+

return (c**(1 - γ) - 1) / (1 - γ)

249+

```

250+251+

Also, here's a default wage distribution, based around the BetaBinomial

252+

distribution:

253+254+

```{code-cell} ipython3

255+

n = 60 # n possible outcomes for w

256+

w_default = jnp.linspace(10, 20, n) # wages between 10 and 20

257+

a, b = 600, 400 # shape parameters

258+

dist = BetaBinomial(n-1, a, b) # distribution

259+

q_default = jnp.array(dist.pdf()) # probabilities as a JAX array

260+

```

261+262+

Here's our model class for the McCall model with separation.

263+264+

```{code-cell} ipython3

265+

class Model(NamedTuple):

266+

α: float = 0.2 # job separation rate

267+

β: float = 0.98 # discount factor

268+

γ: float = 2.0 # utility parameter (CRRA)

269+

c: float = 6.0 # unemployment compensation

270+

w: jnp.ndarray = w_default # wage outcome space

271+

q: jnp.ndarray = q_default # probabilities over wage offers

272+

```

273+274+275+

### Operators

276+277+

To iterate on the Bellman equations, we define two operators, one for each value function.

278+279+

These operators take the current value functions as inputs and return updated versions.

280+281+

```{code-cell} ipython3

282+

def T_u(model, v_u, v_e):

283+

"""

284+

Apply the unemployment Bellman update rule.

285+286+

"""

287+

α, β, γ, c, w, q = model

288+

h = u(c, γ) + β * (v_u @ q)

289+

return jnp.maximum(v_e, h)

290+

```

291+292+

```{code-cell} ipython3

293+

def T_e(model, v_u, v_e):

294+

"""

295+

Apply the employment Bellman update rule.

296+297+

"""

298+

α, β, γ, c, w, q = model

299+

v_e_new = u(w, γ) + β * ((1 - α) * v_e + α * (v_u @ q))

300+

return v_e_new

301+

```

302+303+304+305+

### Iteration

306+307+

Here's our iteration routine, which alternates between updating $v_u$ and $v_e$ until convergence.

308+309+

We iterate until successive realizations are closer together than some small tolerance level.

310+311+

```{code-cell} ipython3

312+

def solve_full_model(

313+

model,

314+

tol: float=1e-6,

315+

max_iter: int=1_000,

316+

):

317+

"""

318+

Solves for both value functions v_u and v_e iteratively.

319+320+

"""

321+

α, β, γ, c, w, q = model

322+

i = 0

323+

error = tol + 1

324+

v_e = v_u = w / (1 - β)

325+326+

while i < max_iter and error > tol:

327+

v_u_next = T_u(model, v_u, v_e)

328+

v_e_next = T_e(model, v_u, v_e)

329+

error_u = jnp.max(jnp.abs(v_u_next - v_u))

330+

error_e = jnp.max(jnp.abs(v_e_next - v_e))

331+

error = jnp.max(jnp.array([error_u, error_e]))

332+

v_u = v_u_next

333+

v_e = v_e_next

334+

i += 1

335+336+

return v_u, v_e

337+

```

338+339+340+341+

### Computing the Reservation Wage

342+343+

Now that we can solve for both value functions, let's compute the reservation wage.

344+345+

Recall from above that the reservation wage $\bar w$ solves

346+

$v_e(\bar w) = h$, where $h$ is the continuation value defined in {eq}`defh_mm`.

347+348+

Let's compare $v_e$ and $h$ to see what they look like.

349+350+

We'll use the default parameterizations found in the code above.

351+352+

```{code-cell} ipython3

353+

model = Model()

354+

α, β, γ, c, w, q = model

355+

v_u, v_e = solve_full_model(model)

356+

h = u(c, γ) + β * (v_u @ q)

357+358+

fig, ax = plt.subplots()

359+

ax.plot(w, v_e, 'b-', lw=2, alpha=0.7, label='$v_e$')

360+

ax.plot(w, [h] * len(w), 'g-', lw=2, alpha=0.7, label='$h$')

361+

ax.set_xlim(min(w), max(w))

362+

ax.legend()

363+

plt.show()

364+

```

365+366+

The value $v_e$ is increasing because higher $w$ generates a higher wage flow conditional on staying employed.

367+368+

The reservation wage is the $w$ where these lines meet.

369+370+

Let's compute this reservation wage explicitly:

371+372+

```{code-cell} ipython3

373+

def compute_reservation_wage_full(model):

374+

"""

375+

Computes the reservation wage using the full model solution.

376+

"""

377+

α, β, γ, c, w, q = model

378+

v_u, v_e = solve_full_model(model)

379+

h = u(c, γ) + β * (v_u @ q)

380+

# Find the first w such that v_e(w) >= h

381+

accept = v_e >= h

382+

i = jnp.argmax(accept)

383+

w_bar = jnp.where(jnp.any(accept), w[i], jnp.inf)

384+

return w_bar

385+386+

w_bar_full = compute_reservation_wage_full(model)

387+

print(f"Reservation wage (full model): {w_bar_full:.4f}")

388+

```

389+390+391+392+393+

(ast_mcm)=

394+

## A Simplifying Transformation

395+396+

The approach above works, but iterating over two vector-valued functions is computationally expensive.

397+398+

Let's return to the key equations and see if we can simplify them to reduce the problem to a single scalar equation.

399+400+

(This process will be analogous to our {ref}`second pass <mm_op2>` at the plain vanilla

401+

McCall model, where we reduced the Bellman equation to an equation in an unknown

402+

scalar value, rather than an unknown vector.)

403+404+

First, recall $h$ as defined in {eq}`defh_mm`.

405+406+

Using $h$, we can now write {eq}`bell2_mccall` as

223407224408

$$

225409

v_u(w) = \max \left\{ v_e(w), \, h \right\}

@@ -291,28 +475,7 @@ h = u(c) + \beta \sum_{w' \in \mathbb W} \max \left\{ \frac{u(w') + \alpha(h - u

291475292476

This is a single scalar equation in $h$.

293477294-

### The Reservation Wage

295-296-

Suppose we can use {eq}`bell_scalar` to solve for $h$.

297-298-

Once we have $h$, we can obtain $v_e$ from {eq}`v_e_closed`.

299478300-

We can then determine optimal behavior for the worker.

301-302-

From {eq}`bell2_mccall`, we see that an unemployed agent accepts current offer

303-

$w$ if $v_e(w) \geq h$.

304-305-

This means precisely that the value of accepting is higher than the value of rejecting.

306-307-

It is clear that $v_e$ is (at least weakly) increasing in $w$, since the agent is never made worse off by a higher wage offer.

308-309-

Hence, we can express the optimal choice as accepting wage offer $w$ if and only if

310-311-

$$

312-

w \geq \bar w

313-

\quad \text{where} \quad

314-

\bar w \text{ solves } v_e(\bar w) = h

315-

$$

316479317480

### Solving the Bellman Equations

318481

@@ -336,50 +499,11 @@ Once convergence is achieved, we can compute $v_e$ from {eq}`v_e_closed`.

336499337500

(It is possible to prove that {eq}`bell_iter` converges via the Banach contraction mapping theorem.)

338501339-

## Implementation

340-341-

Let's implement this iterative process.

342-343-

In the code, you'll see that we use a class to store the various parameters and other

344-

objects associated with a given model.

345-346-

This helps to tidy up the code and provides an object that's easy to pass to functions.

347-348-

The default utility function is a CRRA utility function

349-350-

```{code-cell} ipython3

351-

def u(c, γ):

352-

return (c**(1 - γ) - 1) / (1 - γ)

353-

```

354-355-

Also, here's a default wage distribution, based around the BetaBinomial

356-

distribution:

357-358-

```{code-cell} ipython3

359-

n = 60 # n possible outcomes for w

360-

w_default = jnp.linspace(10, 20, n) # wages between 10 and 20

361-

a, b = 600, 400 # shape parameters

362-

dist = BetaBinomial(n-1, a, b) # distribution

363-

q_default = jnp.array(dist.pdf()) # probabilities as a JAX array

364-

```

365-366-

Here's our model class for the McCall model with separation.

367502368-

```{code-cell} ipython3

369-

class Model(NamedTuple):

370-

α: float = 0.2 # job separation rate

371-

β: float = 0.98 # discount factor

372-

γ: float = 2.0 # utility parameter (CRRA)

373-

c: float = 6.0 # unemployment compensation

374-

w: jnp.ndarray = w_default # wage outcome space

375-

q: jnp.ndarray = q_default # probabilities over wage offers

376-

```

377503378-

Now we iterate until successive realizations are closer together than some small tolerance level.

379-380-

We then return the current iterate as an approximate solution.

504+

## Implementation

381505382-

First, we define a function to compute $v_e$ from $h$:

506+

First, we define a function to compute $v_e$ from $h$ using {eq}`v_e_closed`.

383507384508

```{code-cell} ipython3

385509

def compute_v_e(model, h):

@@ -431,37 +555,6 @@ def solve_model(model, tol=1e-5, max_iter=2000):

431555

return v_e_final, h_final

432556

```

433557434-

### The Reservation Wage: First Pass

435-436-

The optimal choice of the agent is summarized by the reservation wage.

437-438-

As discussed above, the reservation wage is the $\bar w$ that solves

439-

$v_e(\bar w) = h$ where $h$ is the continuation value.

440-441-

Let's compare $v_e$ and $h$ to see what they look like.

442-443-

We'll use the default parameterizations found in the code above.

444-445-

```{code-cell} ipython3

446-

model = Model()

447-

v_e, h = solve_model(model)

448-449-

fig, ax = plt.subplots()

450-

ax.plot(model.w, v_e, 'b-', lw=2, alpha=0.7, label='$v_e$')

451-

ax.plot(model.w, [h] * len(model.w),

452-

'g-', lw=2, alpha=0.7, label='$h$')

453-

ax.set_xlim(min(model.w), max(model.w))

454-

ax.legend()

455-

plt.show()

456-

```

457-458-

The value $v_e$ is increasing because higher $w$ generates a higher wage flow conditional on staying employed.

459-460-461-

The reservation wage is the $w$ where these lines meet.

462-463-464-

### Computing the Reservation Wage

465558466559

Here's a function `compute_reservation_wage` that takes an instance of `Model`

467560

and returns the associated reservation wage.

@@ -470,7 +563,7 @@ and returns the associated reservation wage.

470563

def compute_reservation_wage(model):

471564

"""

472565

Computes the reservation wage of an instance of the McCall model

473-

by finding the smallest w such that v_e(w) >= h.

566+

by finding the smallest w such that v_e(w) >= h.

474567475568

"""

476569

# Find the first i such that v_e(w_i) >= h and return w[i]

@@ -482,6 +575,17 @@ def compute_reservation_wage(model):

482575

return w_bar

483576

```

484577578+

Let's verify that this simplified approach gives the same answer as the full model:

579+580+

```{code-cell} ipython3

581+

w_bar_simplified = compute_reservation_wage(model)

582+

print(f"Reservation wage (simplified): {w_bar_simplified:.4f}")

583+

print(f"Reservation wage (full model): {w_bar_full:.4f}")

584+

print(f"Difference: {abs(w_bar_simplified - w_bar_full):.6f}")

585+

```

586+587+

As we can see, both methods produce essentially the same reservation wage, but the simplified method is much more efficient.

588+485589

Next we will investigate how the reservation wage varies with parameters.

486590487591

Read the original on github.com ↗