@@ -134,7 +134,7 @@ Let
134134Here **maximum lifetime value** means the value of {eq}`objective` when
135135the 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$.
199199200200Once 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$$
225409v_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
291475292476This 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
385509def 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
465558466559Here's a function `compute_reservation_wage` that takes an instance of `Model`
467560and returns the associated reservation wage.
@@ -470,7 +563,7 @@ and returns the associated reservation wage.
470563def 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+485589Next we will investigate how the reservation wage varies with parameters.
486590487591