@@ -512,16 +512,16 @@ We first create a class to set up a particular parametrization.
512512class params_instance:
513513514514 def __init__(self,
515- r,
516- β = 0.999,
517- σ = 0.500,
518- w = 100,
519- n_grid = 50):
515+ r,
516+ β=0.999,
517+ σ=0.500,
518+ w=100,
519+ n_grid=50):
520520521- self.β,self.σ,self.w,self.r = β,σ,w,r
521+ self.β, self.σ, self.w, self.r = β, σ, w, r
522522 self.n_grid = n_grid
523- uw = self.w**(1-self.σ)/(1-self.σ) #Utility from consuming all wage
524- self.Ve = uw/(1-β)
523+ uw = self.w**(1 - self.σ) / (1 - self.σ) # Utility from consuming all wage
524+ self.Ve = uw / (1 - β)
525525```
526526527527### Parameter values
@@ -538,21 +538,25 @@ First, we create some helper functions.
538538539539```{code-cell} ipython3
540540# The probability of finding a job given search effort, a and parameter r.
541-def p(a,r):
542- return 1-np.exp(-r*a)
541+def p(a, r):
542+ return 1 - np.exp(-r * a)
543543544-def invp_prime(x,r):
545- return -np.log(x/r)/r
546544547-def p_prime(a,r):
548- return r*np.exp(-r*a)
545+def invp_prime(x, r):
546+ return -np.log(x / r) / r
549547550-# The utiliy function
551-def u(self,c):
552- return (c**(1-self.σ))/(1-self.σ)
553548554-def u_inv(self,x):
555- return ((1-self.σ)*x)**(1/(1-self.σ))
549+def p_prime(a, r):
550+ return r * np.exp(-r * a)
551+552+553+# The utility function
554+def u(self, c):
555+ return (c**(1 - self.σ)) / (1 - self.σ)
556+557+558+def u_inv(self, x):
559+ return ((1 - self.σ) * x)**(1 / (1 - self.σ))
556560```
557561558562Recall that under autarky the value for an unemployed worker
@@ -585,12 +589,12 @@ We'll soon use this as an input to computing $V^u$.
585589```{code-cell} ipython3
586590# The error in the Bellman equation that requires equality at
587591# the optimal choices.
588-def Vu_error(self,Vu,r):
589- β= self.β
592+def Vu_error(self, Vu, r):
593+ β = self.β
590594 Ve = self.Ve
591595592- a = invp_prime(1/(β*(Ve-Vu)),r)
593- error = u(self,0) -a + β*(p(a,r)*Ve + (1-p(a,r))*Vu) - Vu
596+ a = max(0, invp_prime(1 / (β * (Ve - Vu)), r))
597+ error = u(self, 0) - a + β * (p(a, r) * Ve + (1 - p(a, r)) * Vu) - Vu
594598 return error
595599```
596600@@ -602,36 +606,36 @@ We'll use this to compute a calibrated $r^*$.
602606603607```{code-cell} ipython3
604608# The error of our p(a^*) relative to our calibration target
605-def r_error(self,r):
609+def r_error(self, r):
606610 β = self.β
607611 Ve = self.Ve
608612609- Vu_star = sp.optimize.fsolve(Vu_error_Λ,15000,args = (r))
610- a_star = invp_prime(1/(β*(Ve-Vu_star)),r) # Assuming a>0
611- return p(a_star,r) - 0.1
613+ Vu_star = sp.optimize.fsolve(Vu_error_Λ, 15000, args=(r,))[0]
614+ a_star = invp_prime(1 / (β * (Ve - Vu_star)), r) # Assuming a > 0
615+ return p(a_star, r) - 0.1
612616```
613617614618Now, let us create an instance of the model with our parametrization
615619616620```{code-cell} ipython3
617-params = params_instance(r = 1e-2)
621+params = params_instance(r=1e-2)
618622# Create some lambda functions useful for fsolve function
619-Vu_error_Λ = lambda Vu,r: Vu_error(params,Vu,r)
620-r_error_Λ = lambda r: r_error(params,r)
623+Vu_error_Λ = lambda Vu, r: Vu_error(params, Vu, r)
624+r_error_Λ = lambda r: r_error(params, r)
621625```
622626623627We want to compute an $r$ that is consistent with the hazard rate 0.1 in autarky.
624628625629To do so, we will use a bisection strategy.
626630627631```{code-cell} ipython3
628-r_calibrated = sp.optimize.brentq(r_error_Λ,1e-10,1-1e-10)
632+r_calibrated = sp.optimize.brentq(r_error_Λ, 1e-10, 1 - 1e-10)
629633print(f"Parameter to match 0.1 hazard rate: r = {r_calibrated}")
630634631-Vu_aut = sp.optimize.fsolve(Vu_error_Λ,15000,args = (r_calibrated))[0]
632-a_aut = invp_prime(1/(params.β*(params.Ve-Vu_aut)),r_calibrated)
635+Vu_aut = sp.optimize.fsolve(Vu_error_Λ, 15000, args=(r_calibrated,))[0]
636+a_aut = invp_prime(1 / (params.β * (params.Ve - Vu_aut)), r_calibrated)
633637634-print(f"Check p at r: {p(a_aut,r_calibrated)}")
638+print(f"Check p at r: {p(a_aut, r_calibrated)}")
635639```
636640637641Now that we have calibrated our the parameter $r$, we can continue with solving the model with private information.
@@ -664,25 +668,25 @@ We can substitute these equations for $c$ and $a$ and obtain the functional equa
664668665669666670```{code-cell} ipython3
667-def calc_c(self,Vu,V,a):
671+def calc_c(self, Vu, V, a):
668672 '''
669673 Calculates the optimal consumption choice coming from the constraint of the insurer's problem
670674 (which is also a Bellman equation)
671675 '''
672- β,Ve,r = self.β,self.Ve,self.r
676+ β, Ve, r = self.β, self.Ve, self.r
673677674- c = u_inv(self,V + a - β*(p(a,r)*Ve + (1-p(a,r))*Vu))
678+ c = u_inv(self, V + a - β * (p(a, r) * Ve + (1 - p(a, r)) * Vu))
675679 return c
676680677-def calc_a(self,Vu):
681+682+def calc_a(self, Vu):
678683 '''
679684 Calculates the optimal effort choice coming from the worker's effort optimality condition.
680685 '''
686+ r, β, Ve = self.r, self.β, self.Ve
681687682- r,β,Ve = self.r,self.β,self.Ve
683-684- a_temp = np.log(r*β*(Ve - Vu))/r
685- a = max(0,a_temp)
688+ a_temp = np.log(r * β * (Ve - Vu)) / r
689+ a = max(0, a_temp)
686690 return a
687691```
688692@@ -704,12 +708,11 @@ The function `iterate_C` below executes step 3 in the above algorithm.
704708705709```{code-cell} ipython3
706710# Operator iterate_C that calculates the next iteration of the cost function.
707-def iterate_C(self,C_old,Vu_grid):
708-711+def iterate_C(self, C_old, Vu_grid):
709712 '''
710713 We solve the model by minimising the value function across a grid of possible promised values.
711714 '''
712- β,r,n_grid = self.β,self.r,self.n_grid
715+ β, r, n_grid = self.β, self.r, self.n_grid
713716714717 C_new = np.zeros(n_grid)
715718 cons_star = np.zeros(n_grid)
@@ -725,50 +728,51 @@ def iterate_C(self,C_old,Vu_grid):
725728 a_Vi_temp = np.zeros(n_grid)
726729727730 for Vu_i in range(n_grid):
728- a_i = calc_a(self,Vu_grid[Vu_i])
729- c_i = calc_c(self,Vu_grid[Vu_i],Vu_grid[V_i],a_i)
731+ a_i = calc_a(self, Vu_grid[Vu_i])
732+ c_i = calc_c(self, Vu_grid[Vu_i], Vu_grid[V_i], a_i)
730733731- C_Vi_temp[Vu_i] = c_i + β*(1-p(a_i,r))*C_old[Vu_i]
734+ C_Vi_temp[Vu_i] = c_i + β * (1 - p(a_i, r)) * C_old[Vu_i]
732735 cons_Vi_temp[Vu_i] = c_i
733736 a_Vi_temp[Vu_i] = a_i
734737735738 # Interpolate across the grid to get better approximation of the minimum
736- C_Vi_temp_interp = sp.interpolate.interp1d(Vu_grid,C_Vi_temp, kind = 'cubic')
737- cons_Vi_temp_interp = sp.interpolate.interp1d(Vu_grid,cons_Vi_temp, kind = 'cubic')
738- a_Vi_temp_interp = sp.interpolate.interp1d(Vu_grid,a_Vi_temp, kind = 'cubic')
739+ C_Vi_temp_interp = sp.interpolate.interp1d(Vu_grid, C_Vi_temp, kind='cubic')
740+ cons_Vi_temp_interp = sp.interpolate.interp1d(Vu_grid, cons_Vi_temp, kind='cubic')
741+ a_Vi_temp_interp = sp.interpolate.interp1d(Vu_grid, a_Vi_temp, kind='cubic')
739742740- res = sp.optimize.minimize_scalar(C_Vi_temp_interp,method='bounded',bounds = (Vu_min,Vu_max))
743+ res = sp.optimize.minimize_scalar(C_Vi_temp_interp, method='bounded',
744+ bounds=(Vu_min, Vu_max))
741745 V_star[V_i] = res.x
742746 C_new[V_i] = res.fun
743747744- # Save the associated consumpton and search policy functions as well
748+ # Save the associated consumption and search policy functions as well
745749 cons_star[V_i] = cons_Vi_temp_interp(V_star[V_i])
746750 a_star[V_i] = a_Vi_temp_interp(V_star[V_i])
747751748- return C_new,V_star,cons_star,a_star
752+ return C_new, V_star, cons_star, a_star
749753```
750754751755The following code executes steps 4 and 5 in the Algorithm until convergence to a function $C^*(V)$.
752756753757```{code-cell} ipython3
754-def solve_incomplete_info_model(self,Vu_grid,Vu_aut,tol = 1e-6,max_iter = 10000):
758+def solve_incomplete_info_model(self, Vu_grid, Vu_aut, tol=1e-6, max_iter=10000):
755759 iter = 0
756760 error = 1
757761758- C_init = np.ones(self.n_grid)*0
762+ C_init = np.ones(self.n_grid) * 0
759763 C_old = np.copy(C_init)
760764761- while iter<max_iter and error >tol:
762- C_new,V_new,cons_star,a_star = iterate_C(self,C_old,Vu_grid)
765+ while iter < max_iter and error > tol:
766+ C_new, V_new, cons_star, a_star = iterate_C(self, C_old, Vu_grid)
763767 error = np.max(np.abs(C_new - C_old))
764768765- #Only print the iterations every 50 steps
766- if iter % 50 ==0:
769+ # Only print the iterations every 50 steps
770+ if iter % 50 == 0:
767771 print(f"Iteration: {iter}, error:{error}")
768772 C_old = np.copy(C_new)
769- iter+=1
773+ iter += 1
770774771- return C_new,V_new,cons_star,a_star
775+ return C_new, V_new, cons_star, a_star
772776```
773777774778## Outcomes
@@ -778,22 +782,23 @@ def solve_incomplete_info_model(self,Vu_grid,Vu_aut,tol = 1e-6,max_iter = 10000)
778782Using the above functions, we create another instance of the parameters with our calibrated parameter $r$.
779783780784```{code-cell} ipython3
781-##? Create another instance with the correct r now
782-params = params_instance(r = r_calibrated)
785+# Create another instance with the correct r now
786+params = params_instance(r=r_calibrated)
783787784-#Set up grid
788+# Set up grid
785789Vu_min = Vu_aut
786-Vu_max = params.Ve - 1/(params.β*p_prime(0,params.r))
787-Vu_grid = np.linspace(Vu_min,Vu_max,params.n_grid)
790+Vu_max = params.Ve - 1 / (params.β * p_prime(0, params.r))
791+Vu_grid = np.linspace(Vu_min, Vu_max, params.n_grid)
788792789-#Solve model
790-C_star,V_star,cons_star,a_star = solve_incomplete_info_model(params,Vu_grid,Vu_aut,tol = 1e-6,max_iter = 10000) #,cons_star,a_star
793+# Solve model
794+C_star, V_star, cons_star, a_star = solve_incomplete_info_model(
795+ params, Vu_grid, Vu_aut, tol=1e-6, max_iter=10000)
791796792797# Since we have the policy functions in grid form, we will interpolate them to be able to
793798# evaluate any promised value
794-cons_star_interp = sp.interpolate.interp1d(Vu_grid,cons_star)
795-a_star_interp = sp.interpolate.interp1d(Vu_grid,a_star)
796-V_star_interp = sp.interpolate.interp1d(Vu_grid,V_star)
799+cons_star_interp = sp.interpolate.interp1d(Vu_grid, cons_star)
800+a_star_interp = sp.interpolate.interp1d(Vu_grid, a_star)
801+V_star_interp = sp.interpolate.interp1d(Vu_grid, V_star)
797802```
798803799804### Replacement ratios and continuation values
@@ -809,47 +814,50 @@ We accomplish this by using the optimal policy functions `V_star`, `cons_star` a
809814```{code-cell} ipython3
810815# Replacement ratio and effort as a function of unemployment duration
811816T_max = 52
812-Vu_t = np.empty((T_max,3))
813-cons_t = np.empty((T_max-1,3))
814-a_t = np.empty((T_max-1,3))
817+Vu_t = np.empty((T_max, 3))
818+cons_t = np.empty((T_max - 1, 3))
819+a_t = np.empty((T_max - 1, 3))
815820816821# Calculate the replacement ratios depending on different initial
817822# promised values
818-Vu_0_hold = np.array([Vu_aut,16942,17000])
823+Vu_0_hold = np.array([Vu_aut, 16942, 17000])
819824```
820825821826```{code-cell} ipython3
822-for i,Vu_0, in enumerate(Vu_0_hold):
823- Vu_t[0,i] = Vu_0
824- for t in range(1,T_max):
825- cons_t[t-1,i] = cons_star_interp(Vu_t[t-1,i])
826- a_t[t-1,i] = a_star_interp(Vu_t[t-1,i])
827- Vu_t[t,i] = V_star_interp(Vu_t[t-1,i])
827+for i, Vu_0 in enumerate(Vu_0_hold):
828+ Vu_t[0, i] = Vu_0
829+ for t in range(1, T_max):
830+ cons_t[t - 1, i] = cons_star_interp(Vu_t[t - 1, i])
831+ a_t[t - 1, i] = a_star_interp(Vu_t[t - 1, i])
832+ Vu_t[t, i] = V_star_interp(Vu_t[t - 1, i])
828833```
829834830835```{code-cell} ipython3
831836fontSize = 10
832-plt.rc('font', size=fontSize) # controls default text sizes
833-plt.rc('axes', titlesize=fontSize) # fontsize of the axes title
834-plt.rc('axes', labelsize=fontSize) # fontsize of the x and y labels
835-plt.rc('xtick', labelsize=fontSize) # fontsize of the tick labels
836-plt.rc('ytick', labelsize=fontSize) # fontsize of the tick labels
837-plt.rc('legend', fontsize=fontSize) # legend fontsize
838-839-f1 = plt.figure(figsize = (8,8))
840-plt.subplot(2,1,1)
841-plt.plot(range(T_max-1),cons_t[:,0]/params.w,label = '$V^u_0$ = 16759 (aut)',color = 'red')
842-plt.plot(range(T_max-1),cons_t[:,1]/params.w,label = '$V^u_0$ = 16942',color = 'blue')
843-plt.plot(range(T_max-1),cons_t[:,2]/params.w,label = '$V^u_0$ = 17000',color = 'green')
837+plt.rc('font', size=fontSize) # controls default text sizes
838+plt.rc('axes', titlesize=fontSize) # fontsize of the axes title
839+plt.rc('axes', labelsize=fontSize) # fontsize of the x and y labels
840+plt.rc('xtick', labelsize=fontSize) # fontsize of the tick labels
841+plt.rc('ytick', labelsize=fontSize) # fontsize of the tick labels
842+plt.rc('legend', fontsize=fontSize) # legend fontsize
843+844+f1 = plt.figure(figsize=(8, 8))
845+plt.subplot(2, 1, 1)
846+plt.plot(range(T_max - 1), cons_t[:, 0] / params.w,
847+ label='$V^u_0$ = 16759 (aut)', color='red')
848+plt.plot(range(T_max - 1), cons_t[:, 1] / params.w,
849+ label='$V^u_0$ = 16942', color='blue')
850+plt.plot(range(T_max - 1), cons_t[:, 2] / params.w,
851+ label='$V^u_0$ = 17000', color='green')
844852plt.ylabel("Replacement ratio (c/w)")
845853plt.legend()
846854plt.title("Optimal replacement ratio")
847855848-plt.subplot(2,1,2)
849-plt.plot(range(T_max-1),a_t[:,0],color = 'red')
850-plt.plot(range(T_max-1),a_t[:,1],color = 'blue')
851-plt.plot(range(T_max-1),a_t[:,2],color = 'green')
852-plt.ylim(0,320)
856+plt.subplot(2, 1, 2)
857+plt.plot(range(T_max - 1), a_t[:, 0], color='red')
858+plt.plot(range(T_max - 1), a_t[:, 1], color='blue')
859+plt.plot(range(T_max - 1), a_t[:, 2], color='green')
860+plt.ylim(0, 320)
853861plt.ylabel("Optimal search effort (a)")
854862plt.xlabel("Duration of unemployment")
855863plt.title("Optimal search effort")