def solveWorkingDeaton(solution_next, aXtraGrid, mGrid, EGMVector, par, Util, UtilP, UtilP_inv, TranInc, TranIncWeights): choice = 2 choiceCount = len(solution_next.ChoiceSols) # Next-period initial wealth given exogenous aXtraGrid # This needs to be made more general like the rest of the code mrs_tp1 = par.Rfree * numpy.expand_dims(aXtraGrid, axis=1) + par.YWork * TranInc.T mws_tp1 = par.Rfree * numpy.expand_dims(aXtraGrid, axis=1) + par.YWork * TranInc.T m_tp1s = (mrs_tp1, mws_tp1) # Prepare variables for EGM step C_tp1s = tuple(solution_next.ChoiceSols[d].CFunc(m_tp1s[d]) for d in range(choiceCount)) Vs = tuple( numpy.divide(-1.0, solution_next.ChoiceSols[d].V_TFunc(m_tp1s[d])) for d in range(choiceCount)) # Due to the transformation on V being monotonic increasing, we can just as # well use the transformed values to do this discrete envelope step. V, ChoiceProb_tp1 = calcLogSumChoiceProbs(numpy.stack(Vs), par.sigma) # Calculate the expected marginal utility and expected value function PUtilPsum = sum(ChoiceProb_tp1[i, :] * UtilP(C_tp1s[i], i + 1) for i in range(choiceCount)) EUtilP_tp1 = par.Rfree * numpy.dot(PUtilPsum, TranIncWeights.T) EV_tp1 = numpy.squeeze( numpy.dot(numpy.expand_dims(V, axis=1), TranIncWeights.T)) # EGM step m_t, C_t, Ev = calcEGMStep(EGMVector, aXtraGrid, EV_tp1, EUtilP_tp1, par, Util, UtilP, UtilP_inv, choice) V_T = numpy.divide(-1.0, Util(C_t, choice) + par.DiscFac * Ev) # We do the envelope step in transformed value space for accuracy. The values # keep their monotonicity under our transformation. m_t, C_t, V_T = calcMultilineEnvelope(m_t, C_t, V_T, mGrid) # The solution is the working specific consumption function and value function # specifying lower_extrap=True for C is easier than explicitly adding a 0, # as it'll be linear in the constrained interval anyway. CFunc = LinearInterp(m_t, C_t, lower_extrap=True) V_TFunc = LinearInterp(m_t, V_T, lower_extrap=True) return ChoiceSpecificSolution(m_t, C_t, CFunc, V_T, V_TFunc)
def test_crossing(self): # Test that the upper envelope has the approximate correct value # where the two increasing segments with m_1 = [2, 3] and m_2 = [2.0, 4.0] # is the correct value. # # Calculate the crossing by hand slope_1 = (1.5 - 1.0)/(3.0 - 2.0) slope_2 = (3.5 - 0.5)/(4.0 - 2.0) m_cross = 2.0 + (0.5 - 1.0)/(slope_1 - slope_2) m_out, c_out, v_out = dcegm.calcMultilineEnvelope(self.m_in, self.c_in, self.v_in, self.commonM) m_idx = 0 for m in m_out: if m > m_cross: break m_idx += 1 # Just right of the cross, the second segment is optimal true_v = 0.5 + (m_out[m_idx] - 2.0)*slope_2 self.assertTrue(abs(v_out[m_idx] - true_v) < 1e-12)
# We see that the last segment is increasing (as the last element of `rise` is larger than the last element of `fall`), and we see that `len(fall)` is one larger than number of problematic segments in the plot. The index of the last point in `m_egm`/`c_egm`/`vt_egm` is added for convenience when we do the upper envelope step (and is also convenient below for drawing the segments!). # # We can use `fall` and `rise` to draw only the relevant segments that we will use to construct an upper envelope. # %% for j in range(len(fall)): idx = range(rise[j], fall[j] + 1) plt.plot(m_egm[idx], vt_egm[idx]) plt.xlabel("resources") plt.ylabel("transformed values") plt.show() # %% [markdown] # Let us now use the `calcMultilineEnvelope` function to do the full DCEGM step: find segments and calculate upper envelope in one sweep. # %% m_upper, c_upper, v_upper = calcMultilineEnvelope(m_egm, c_egm, vt_egm, m_common) # %% for j in range(len(fall)): idx = range(rise[j], fall[j] + 1) plt.plot(m_egm[idx], vt_egm[idx]) plt.plot(m_upper, v_upper, 'k') plt.xlabel("resources") plt.ylabel("transformed values") plt.show() # %% [markdown] # And there we have it! These functions are the building blocks for univariate discrete choice modeling in HARK, so hopefully this little demo helped better understand what goes on under the hood, or it was a help if you're extending some existing class with a discrete choice. # %% [markdown] # # An example: writing a will #
# We see that the last segment is increasing (as the last element of `rise` is larger than the last element of `fall`), and we see that `len(fall)` is one larger than number of problematic segments in the plot. The index of the last point in `m_egm`/`c_egm`/`vt_egm` is added for convenience when we do the upper envelope step (and is also convenient below for drawing the segments!). # # We can use `fall` and `rise` to draw only the relevant segments that we will use to construct an upper envelope. # %% for j in range(len(fall)): idx = range(rise[j], fall[j] + 1) plt.plot(m_egm[idx], vt_egm[idx]) plt.xlabel("resources") plt.ylabel("transformed values") plt.show() # %% [markdown] # Let us now use the `calcMultilineEnvelope` function to do the full DCEGM step: find segments and calculate upper envelope in one sweep. # %% m_upper, c_upper, v_upper = calcMultilineEnvelope(m_egm, c_egm, vt_egm, m_common) # %% for j in range(len(fall)): idx = range(rise[j], fall[j] + 1) plt.plot(m_egm[idx], vt_egm[idx]) plt.plot(m_upper, v_upper, 'k') plt.xlabel("resources") plt.ylabel("transformed values") plt.show() # %% [markdown] # And there we have it! These functions are the building blocks for univariate discrete choice modeling in HARK, so hopefully this little demo helped better understand what goes on under the hood, or it was a help if you're extending some existing class with a discrete choice. # %% [markdown] # # An example: writing a will #
fall # We see that the last segment is increasing (as the last element of `rise` is larger than the last element of `fall`), and we see that `len(fall)` is one larger than number of problematic segments in the plot. The index of the last point in `m_egm`/`c_egm`/`vt_egm` is added for convenience when we do the upper envelope step (and is also convenient below for drawing the segments!). # # We can use `fall` and `rise` to draw only the relevant segments that we will use to construct an upper envelope. for j in range(len(fall)): idx = range(rise[j],fall[j]+1) plt.plot(m_egm[idx], vt_egm[idx]) plt.xlabel("resources") plt.ylabel("transformed values") # Let us now use the `calcMultilineEnvelope` function to do the full DCEGM step: find segments and calculate upper envelope in one sweep. m_upper, c_upper, v_upper = calcMultilineEnvelope(m_egm, c_egm, vt_egm, m_common) for j in range(len(fall)): idx = range(rise[j],fall[j]+1) plt.plot(m_egm[idx], vt_egm[idx]) plt.plot(m_upper, v_upper, 'k') plt.xlabel("resources") plt.ylabel("transformed values") # And there we have it! These functions are the building blocks for univariate discrete choice modeling in HARK, so hopefully this little demo helped better understand what goes on under the hood, or it was a help if you're extending some existing class with a discrete choice. # # References # [1] Iskhakov, F. , Jørgensen, T. H., Rust, J. and Schjerning, B. (2017), The endogenous grid method for discrete‐continuous dynamic choice models with (or without) taste shocks. Quantitative Economics, 8: 317-365. doi:10.3982/QE643 # # [2] Carroll, C. D. (2006). The method of endogenous gridpoints for solving dynamic stochastic optimization problems. Economics letters, 91(3), 312-320. #