Skip to content

Commit

Permalink
Added vFunc capability to simple ConsMarkov solver
Browse files Browse the repository at this point in the history
Also fixed a bug/error in the existing vFunc code that made it incapable of actually solving. The good news is that the new code exactly replicates the old code. The bad news is that something is wrong with the vFunc extrapolation code, identically in both versions. Shouldn't be too hard to fix.
  • Loading branch information
mnwhite committed Mar 13, 2024
1 parent 8d3cc8d commit 00861e6
Showing 1 changed file with 62 additions and 21 deletions.
83 changes: 62 additions & 21 deletions HARK/ConsumptionSaving/ConsMarkovModel.py
Original file line number Diff line number Diff line change
Expand Up @@ -276,17 +276,25 @@ def calc_vPPnext(S, a, R):
# period's state and add it to the list of value functions
if vFuncBool:
# Calculate end-of-period value, its derivative, and their pseudo-inverse
BegOfPrd_vNext = DiscFacEff * expected(calc_vNext, IncShkDstn, args=(aNrmNext, Rfree))
BegOfPrd_vNext = DiscFacEff * expected(

Check warning on line 279 in HARK/ConsumptionSaving/ConsMarkovModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsMarkovModel.py#L279

Added line #L279 was not covered by tests
calc_vNext, IncShkDstn, args=(aNrmNext, Rfree)
)
# value transformed through inverse utility
BegOfPrd_vNvrsNext = uFunc.inv(BegOfPrd_vNext)
BegOfPrd_vNvrsPnext = BegOfPrd_vPnext * uFunc.derinv(BegOfPrd_vNext, order=(0, 1))
BegOfPrd_vNvrsNext = uFunc.inv(BegOfPrd_vNext)
BegOfPrd_vNvrsPnext = BegOfPrd_vPnext * uFunc.derinv(

Check warning on line 284 in HARK/ConsumptionSaving/ConsMarkovModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsMarkovModel.py#L283-L284

Added lines #L283 - L284 were not covered by tests
BegOfPrd_vNext, order=(0, 1)
)
BegOfPrd_vNvrsNext = np.insert(BegOfPrd_vNvrsNext, 0, 0.0)
BegOfPrd_vNvrsPnext = np.insert(BegOfPrd_vNvrsPnext, 0, BegOfPrd_vNvrsPnext[0])
BegOfPrd_vNvrsPnext = np.insert(

Check warning on line 288 in HARK/ConsumptionSaving/ConsMarkovModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsMarkovModel.py#L287-L288

Added lines #L287 - L288 were not covered by tests
BegOfPrd_vNvrsPnext, 0, BegOfPrd_vNvrsPnext[0]
)
# This is a very good approximation, vNvrsPP = 0 at the asset minimum

# Construct the end-of-period value function
aNrm_temp = np.insert(aNrmNext, 0, BoroCnstNat)
BegOfPrd_vNvrsFunc = CubicInterp(aNrm_temp, BegOfPrd_vNvrsNext, BegOfPrd_vNvrsPnext)
BegOfPrd_vNvrsFunc = CubicInterp(

Check warning on line 295 in HARK/ConsumptionSaving/ConsMarkovModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsMarkovModel.py#L294-L295

Added lines #L294 - L295 were not covered by tests
aNrm_temp, BegOfPrd_vNvrsNext, BegOfPrd_vNvrsPnext
)
BegOfPrd_vFunc = ValueFuncCRRA(BegOfPrd_vNvrsFunc, CRRA)
BegOfPrd_vFunc_list.append(BegOfPrd_vFunc)

Check warning on line 299 in HARK/ConsumptionSaving/ConsMarkovModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsMarkovModel.py#L298-L299

Added lines #L298 - L299 were not covered by tests

Expand Down Expand Up @@ -398,7 +406,7 @@ def calc_vPPnext(S, a, R):

# Calculate the MPC at each market resource gridpoint in each state (if desired)
if CubicBool:
dcda = EndOfPrd_vPP / uFunc.der(cNrmNow[:,1:], order=2) # drop first
dcda = EndOfPrd_vPP / uFunc.der(cNrmNow[:, 1:], order=2) # drop first
MPCnow = dcda / (dcda + 1.0)
MPCnow = np.hstack((np.reshape(MPCmaxNow, (StateCountNow, 1)), MPCnow))

Expand All @@ -411,6 +419,7 @@ def calc_vPPnext(S, a, R):
# Set current-Markov-state-conditional human wealth and MPC bounds
hNrmNow_i = hNrmNow[i]
MPCminNow_i = MPCminNow[i]
mNrmMin_i = mNrmMin_list[i]

# Construct the consumption function by combining the constrained and unconstrained portions
cFuncNowCnst = LinearInterp(
Expand All @@ -437,30 +446,62 @@ def calc_vPPnext(S, a, R):
else:
vPPfuncNow = NullFunc()

# Make the value function for this state if requested
if vFuncBool:
# Make state-conditional grids of market resources and consumption
mNrm_for_vFunc = mNrmMin_i + aXtraGrid
cNrm_for_vFunc = cFuncNow(mNrm_for_vFunc)
aNrm_for_vFunc = mNrm_for_vFunc - cNrm_for_vFunc

Check warning on line 454 in HARK/ConsumptionSaving/ConsMarkovModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsMarkovModel.py#L452-L454

Added lines #L452 - L454 were not covered by tests

# Calculate end-of-period value at each gridpoint
BegOfPrd_v_temp = np.zeros((StateCountNow, aXtraGrid.size))
for j in range(StateCountNext):
if possible_transitions[i, j]:
BegOfPrd_v_temp[j, :] = BegOfPrd_vFunc_list[j](aNrm_for_vFunc)
EndOfPrd_v = np.dot(MrkvArray[i, :], BegOfPrd_v_temp)

Check warning on line 461 in HARK/ConsumptionSaving/ConsMarkovModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsMarkovModel.py#L457-L461

Added lines #L457 - L461 were not covered by tests

# Calculate (normalized) value and marginal value at each gridpoint
v_now = uFunc(cNrm_for_vFunc) + EndOfPrd_v
vP_now = uFunc.der(cNrm_for_vFunc)

Check warning on line 465 in HARK/ConsumptionSaving/ConsMarkovModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsMarkovModel.py#L464-L465

Added lines #L464 - L465 were not covered by tests

# Make a "decurved" value function with the inverse utility function
# value transformed through inverse utility
vNvrs_now = uFunc.inv(v_now)
vNvrsP_now = vP_now * uFunc.derinv(v_now, order=(0, 1))
mNrm_temp = np.insert(mNrm_for_vFunc, 0, mNrmMin_i) # add the lower bound
vNvrs_now = np.insert(vNvrs_now, 0, 0.0)
vNvrsP_now = np.insert(

Check warning on line 473 in HARK/ConsumptionSaving/ConsMarkovModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsMarkovModel.py#L469-L473

Added lines #L469 - L473 were not covered by tests
vNvrsP_now, 0, MPCmaxEff[i] ** (-CRRA / (1.0 - CRRA))
)
MPCminNvrs = MPCminNow[i] ** (-CRRA / (1.0 - CRRA))
vNvrsFuncNow = CubicInterp(

Check warning on line 477 in HARK/ConsumptionSaving/ConsMarkovModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsMarkovModel.py#L476-L477

Added lines #L476 - L477 were not covered by tests
mNrm_temp, vNvrs_now, vNvrsP_now, MPCminNvrs * hNrmNow_i, MPCminNvrs
)

# "Recurve" the decurved value function and add it to the list
vFuncNow = ValueFuncCRRA(vNvrsFuncNow, CRRA)

Check warning on line 482 in HARK/ConsumptionSaving/ConsMarkovModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsMarkovModel.py#L482

Added line #L482 was not covered by tests

else:
vFuncNow = NullFunc()

# Make the current-Markov-state-conditional solution
solution_cond = ConsumerSolution(
cFunc=cFuncNow,
vFunc=vFuncNow,
vPfunc=vPfuncNow,
vPPfunc=vPPfuncNow,
mNrmMin=mNrmMin_list[i],
mNrmMin=mNrmMin_i,
)

# Add the current-state-conditional solution to the overall period solution
solution.append_solution(solution_cond)

# Add the lower bounds of market resources, MPC limits, human resources,
# and the value functions to the overall solution
# and the value functions to the overall solution, then return it
solution.mNrmMin = mNrmMin_list
solution.hNrm = hNrmNow
solution.MPCmin = MPCminNow
solution.MPCmax = MPCmaxNow

if vFuncBool:
pass
# vFuncNow = make_vFunc(solution)
# solution.vFunc = vFuncNow
# TODO: Add vFunc functionality

return solution


Expand Down Expand Up @@ -769,11 +810,12 @@ def make_EndOfPrdvFuncCond(self):

def v_lvl_next(shocks, a_nrm, Rfree):
return (
shocks[0] ** (1.0 - self.CRRA) * self.PermGroFac ** (1.0 - self.CRRA)
shocks["PermShk"] ** (1.0 - self.CRRA)
* self.PermGroFac ** (1.0 - self.CRRA)
) * self.vFuncNext(self.m_nrm_next(shocks, a_nrm, Rfree))

EndOfPrdv_cond = self.DiscFacEff * calc_expectation(
self.IncShkDstn, v_lvl_next, self.aNrmNow, self.Rfree
EndOfPrdv_cond = self.DiscFacEff * self.IncShkDstn.expected(

Check warning on line 817 in HARK/ConsumptionSaving/ConsMarkovModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsMarkovModel.py#L817

Added line #L817 was not covered by tests
v_lvl_next, self.aNrmNow, self.Rfree
)
EndOfPrdvNvrs = self.u.inv(
EndOfPrdv_cond
Expand Down Expand Up @@ -892,7 +934,7 @@ def calc_EndOfPrdvP(self):
): # only consider a future state if one of the relevant states could transition to it
EndOfPrdvP_all[j, :] = self.EndOfPrdvPfunc_list[j](aGrid)
# Add conditional end-of-period (marginal) marginal value to the arrays
if self.CubicBool:
if self.CubicBool:

Check warning on line 937 in HARK/ConsumptionSaving/ConsMarkovModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsMarkovModel.py#L937

Added line #L937 was not covered by tests
EndOfPrdvPP_all[j, :] = self.EndOfPrdvPfunc_list[j].derivativeX(
aGrid
)
Expand Down Expand Up @@ -1264,8 +1306,7 @@ class MarkovConsumerType(IndShockConsumerType):

def __init__(self, **kwds):
IndShockConsumerType.__init__(self, **kwds)
self.solve_one_period = _solve_ConsMarkov
# self.solve_one_period = solve_one_period_ConsMarkov
self.solve_one_period = solve_one_period_ConsMarkov

if not hasattr(self, "global_markov"):
self.global_markov = False
Expand Down

0 comments on commit 00861e6

Please sign in to comment.