Skip to content

Commit

Permalink
Add simple solver for WarmGlowBequest
Browse files Browse the repository at this point in the history
This was very easy, basically a copy-paste of IndShock's simple solver with 3 lines added. Removed all the cubic and vFunc stuff to match current capability.
  • Loading branch information
mnwhite committed Mar 13, 2024
1 parent ea15b11 commit 056cbd6
Show file tree
Hide file tree
Showing 2 changed files with 190 additions and 6 deletions.
191 changes: 188 additions & 3 deletions HARK/ConsumptionSaving/ConsBequestModel.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@
"""

import numpy as np
from HARK import NullFunc

Check warning on line 13 in HARK/ConsumptionSaving/ConsBequestModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsBequestModel.py#L13

Added line #L13 was not covered by tests
from HARK.ConsumptionSaving.ConsIndShockModel import (
ConsIndShockSolver,
ConsumerSolution,
IndShockConsumerType,
init_idiosyncratic_shocks,
init_lifecycle,
Expand All @@ -23,10 +25,12 @@
init_portfolio,
)
from HARK.core import make_one_period_oo_solver
from HARK.distribution import expected

Check warning on line 28 in HARK/ConsumptionSaving/ConsBequestModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsBequestModel.py#L28

Added line #L28 was not covered by tests
from HARK.interpolation import (
ConstantFunction,
IdentityFunction,
LinearInterp,
LowerEnvelope,
MargMargValueFuncCRRA,
MargValueFuncCRRA,
ValueFuncCRRA,
Expand All @@ -50,9 +54,7 @@ def __init__(self, **kwds):

super().__init__(**params)

self.solve_one_period = make_one_period_oo_solver(
BequestWarmGlowConsumerSolver,
)
self.solve_one_period = solve_one_period_ConsWarmBequest

Check warning on line 57 in HARK/ConsumptionSaving/ConsBequestModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsBequestModel.py#L57

Added line #L57 was not covered by tests

def update(self):
super().update()
Expand Down Expand Up @@ -219,6 +221,189 @@ def update_solution_terminal(self):
)


def solve_one_period_ConsWarmBequest(

Check warning on line 224 in HARK/ConsumptionSaving/ConsBequestModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsBequestModel.py#L224

Added line #L224 was not covered by tests
solution_next,
IncShkDstn,
LivPrb,
DiscFac,
CRRA,
Rfree,
PermGroFac,
BoroCnstArt,
aXtraGrid,
BeqCRRA,
BeqFac,
BeqShift,
):
"""
Solves one period of a consumption-saving model with idiosyncratic shocks to
permanent and transitory income, with one risk free asset and CRRA utility.
The consumer also has a "warm glow" bequest motive in which they gain additional
utility based on their terminal wealth upon death. Currently does not support
value function construction nor cubic spline interpolation, in order to match
behavior of existing OO-solver.
Parameters
----------
solution_next : ConsumerSolution
The solution to next period's one period problem.
IncShkDstn : distribution.Distribution
A discrete approximation to the income process between the period being
solved and the one immediately following (in solution_next).
LivPrb : float
Survival probability; likelihood of being alive at the beginning of
the succeeding period.
DiscFac : float
Intertemporal discount factor for future utility.
CRRA : float
Coefficient of relative risk aversion.
Rfree : float
Risk free interest factor on end-of-period assets.
PermGroFac : float
Expected permanent income growth factor at the end of this period.
BoroCnstArt: float or None
Borrowing constraint for the minimum allowable assets to end the
period with. If it is less than the natural borrowing constraint,
then it is irrelevant; BoroCnstArt=None indicates no artificial bor-
rowing constraint.
aXtraGrid : np.array
Array of "extra" end-of-period asset values-- assets above the
absolute minimum acceptable level.
BeqCRRA : float
Coefficient of relative risk aversion for warm glow bequest motive.
BeqFac : float
Multiplicative intensity factor for the warm glow bequest motive.
BeqShift : float
Stone-Geary shifter in the warm glow bequest motive.
Returns
-------
solution_now : ConsumerSolution
Solution to this period's consumption-saving problem with income risk.
"""
# Define the current period utility function and effective discount factor
uFunc = UtilityFuncCRRA(CRRA)
DiscFacEff = DiscFac * LivPrb # "effective" discount factor
BeqFacEff = (1.0 - LivPrb) * BeqFac # "effective" bequest factor
warm_glow = UtilityFuncStoneGeary(BeqCRRA, BeqFacEff, BeqShift)

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

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsBequestModel.py#L285-L288

Added lines #L285 - L288 were not covered by tests

# Unpack next period's income shock distribution
ShkPrbsNext = IncShkDstn.pmv
PermShkValsNext = IncShkDstn.atoms[0]
TranShkValsNext = IncShkDstn.atoms[1]
PermShkMinNext = np.min(PermShkValsNext)
TranShkMinNext = np.min(TranShkValsNext)

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

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsBequestModel.py#L291-L295

Added lines #L291 - L295 were not covered by tests

# Calculate the probability that we get the worst possible income draw
IncNext = PermShkValsNext * TranShkValsNext
WorstIncNext = PermShkMinNext * TranShkMinNext
WorstIncPrb = np.sum(ShkPrbsNext[IncNext == WorstIncNext])

Check warning on line 300 in HARK/ConsumptionSaving/ConsBequestModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsBequestModel.py#L298-L300

Added lines #L298 - L300 were not covered by tests
# WorstIncPrb is the "Weierstrass p" concept: the odds we get the WORST thing

# Unpack next period's (marginal) value function
vFuncNext = solution_next.vFunc # This is None when vFuncBool is False
vPfuncNext = solution_next.vPfunc
vPPfuncNext = solution_next.vPPfunc # This is None when CubicBool is False

Check warning on line 306 in HARK/ConsumptionSaving/ConsBequestModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsBequestModel.py#L304-L306

Added lines #L304 - L306 were not covered by tests

# Update the bounding MPCs and PDV of human wealth:
PatFac = ((Rfree * DiscFacEff) ** (1.0 / CRRA)) / Rfree
try:
MPCminNow = 1.0 / (1.0 + PatFac / solution_next.MPCmin)
except:
MPCminNow = 0.0
Ex_IncNext = np.dot(ShkPrbsNext, TranShkValsNext * PermShkValsNext)
hNrmNow = PermGroFac / Rfree * (Ex_IncNext + solution_next.hNrm)
temp_fac = (WorstIncPrb ** (1.0 / CRRA)) * PatFac
MPCmaxNow = 1.0 / (1.0 + temp_fac / solution_next.MPCmax)
cFuncLimitIntercept = MPCminNow * hNrmNow
cFuncLimitSlope = MPCminNow

Check warning on line 319 in HARK/ConsumptionSaving/ConsBequestModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsBequestModel.py#L309-L319

Added lines #L309 - L319 were not covered by tests

# Calculate the minimum allowable value of money resources in this period
PermGroFacEffMin = (PermGroFac * PermShkMinNext) / Rfree
BoroCnstNat = (solution_next.mNrmMin - TranShkMinNext) * PermGroFacEffMin
BoroCnstNat = np.max([BoroCnstNat, -BeqShift])

Check warning on line 324 in HARK/ConsumptionSaving/ConsBequestModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsBequestModel.py#L322-L324

Added lines #L322 - L324 were not covered by tests

# Set the minimum allowable (normalized) market resources based on the natural
# and artificial borrowing constraints
if BoroCnstArt is None:
mNrmMinNow = BoroCnstNat

Check warning on line 329 in HARK/ConsumptionSaving/ConsBequestModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsBequestModel.py#L328-L329

Added lines #L328 - L329 were not covered by tests
else:
mNrmMinNow = np.max([BoroCnstNat, BoroCnstArt])

Check warning on line 331 in HARK/ConsumptionSaving/ConsBequestModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsBequestModel.py#L331

Added line #L331 was not covered by tests

# Set the upper limit of the MPC (at mNrmMinNow) based on whether the natural
# or artificial borrowing constraint actually binds
if BoroCnstNat < mNrmMinNow:
MPCmaxEff = 1.0 # If actually constrained, MPC near limit is 1

Check warning on line 336 in HARK/ConsumptionSaving/ConsBequestModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsBequestModel.py#L335-L336

Added lines #L335 - L336 were not covered by tests
else:
MPCmaxEff = MPCmaxNow # Otherwise, it's the MPC calculated above

Check warning on line 338 in HARK/ConsumptionSaving/ConsBequestModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsBequestModel.py#L338

Added line #L338 was not covered by tests

# Define the borrowing-constrained consumption function
cFuncNowCnst = LinearInterp(

Check warning on line 341 in HARK/ConsumptionSaving/ConsBequestModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsBequestModel.py#L341

Added line #L341 was not covered by tests
np.array([mNrmMinNow, mNrmMinNow + 1.0]), np.array([0.0, 1.0])
)

# Construct the assets grid by adjusting aXtra by the natural borrowing constraint
aNrmNow = np.asarray(aXtraGrid) + BoroCnstNat

Check warning on line 346 in HARK/ConsumptionSaving/ConsBequestModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsBequestModel.py#L346

Added line #L346 was not covered by tests

# Define local functions for taking future expectations
def calc_mNrmNext(S, a, R):
return R / (PermGroFac * S["PermShk"]) * a + S["TranShk"]

Check warning on line 350 in HARK/ConsumptionSaving/ConsBequestModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsBequestModel.py#L349-L350

Added lines #L349 - L350 were not covered by tests

def calc_vNext(S, a, R):
return (S["PermShk"] ** (1.0 - CRRA) * PermGroFac ** (1.0 - CRRA)) * vFuncNext(

Check warning on line 353 in HARK/ConsumptionSaving/ConsBequestModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsBequestModel.py#L352-L353

Added lines #L352 - L353 were not covered by tests
calc_mNrmNext(S, a, R)
)

def calc_vPnext(S, a, R):
return S["PermShk"] ** (-CRRA) * vPfuncNext(calc_mNrmNext(S, a, R))

Check warning on line 358 in HARK/ConsumptionSaving/ConsBequestModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsBequestModel.py#L357-L358

Added lines #L357 - L358 were not covered by tests

def calc_vPPnext(S, a, R):
return S["PermShk"] ** (-CRRA - 1.0) * vPPfuncNext(calc_mNrmNext(S, a, R))

Check warning on line 361 in HARK/ConsumptionSaving/ConsBequestModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsBequestModel.py#L360-L361

Added lines #L360 - L361 were not covered by tests

# Calculate end-of-period marginal value of assets at each gridpoint
vPfacEff = DiscFacEff * Rfree * PermGroFac ** (-CRRA)
EndOfPrdvP = vPfacEff * expected(calc_vPnext, IncShkDstn, args=(aNrmNow, Rfree))
EndOfPrdvP += warm_glow.der(aNrmNow)

Check warning on line 366 in HARK/ConsumptionSaving/ConsBequestModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsBequestModel.py#L364-L366

Added lines #L364 - L366 were not covered by tests

# Invert the first order condition to find optimal cNrm from each aNrm gridpoint
cNrmNow = uFunc.derinv(EndOfPrdvP, order=(1, 0))
mNrmNow = cNrmNow + aNrmNow # Endogenous mNrm gridpoints

Check warning on line 370 in HARK/ConsumptionSaving/ConsBequestModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsBequestModel.py#L369-L370

Added lines #L369 - L370 were not covered by tests

# Limiting consumption is zero as m approaches mNrmMin
c_for_interpolation = np.insert(cNrmNow, 0, 0.0)
m_for_interpolation = np.insert(mNrmNow, 0, BoroCnstNat)

Check warning on line 374 in HARK/ConsumptionSaving/ConsBequestModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsBequestModel.py#L373-L374

Added lines #L373 - L374 were not covered by tests

# Construct the unconstrained consumption function as a linear interpolation
cFuncNowUnc = LinearInterp(

Check warning on line 377 in HARK/ConsumptionSaving/ConsBequestModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsBequestModel.py#L377

Added line #L377 was not covered by tests
m_for_interpolation,
c_for_interpolation,
cFuncLimitIntercept,
cFuncLimitSlope,
)

# Combine the constrained and unconstrained functions into the true consumption function.
# LowerEnvelope should only be used when BoroCnstArt is True
cFuncNow = LowerEnvelope(cFuncNowUnc, cFuncNowCnst, nan_bool=False)

Check warning on line 386 in HARK/ConsumptionSaving/ConsBequestModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsBequestModel.py#L386

Added line #L386 was not covered by tests

# Make the marginal value function and the marginal marginal value function
vPfuncNow = MargValueFuncCRRA(cFuncNow, CRRA)
vPPfuncNow = NullFunc() # Dummy object
vFuncNow = NullFunc() # Dummy object

Check warning on line 391 in HARK/ConsumptionSaving/ConsBequestModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsBequestModel.py#L389-L391

Added lines #L389 - L391 were not covered by tests

# Create and return this period's solution
solution_now = ConsumerSolution(

Check warning on line 394 in HARK/ConsumptionSaving/ConsBequestModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsBequestModel.py#L394

Added line #L394 was not covered by tests
cFunc=cFuncNow,
vFunc=vFuncNow,
vPfunc=vPfuncNow,
vPPfunc=vPPfuncNow,
mNrmMin=mNrmMinNow,
hNrm=hNrmNow,
MPCmin=MPCminNow,
MPCmax=MPCmaxEff,
)
return solution_now

Check warning on line 404 in HARK/ConsumptionSaving/ConsBequestModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsBequestModel.py#L404

Added line #L404 was not covered by tests


class BequestWarmGlowConsumerSolver(ConsIndShockSolver):
def __init__(
self,
Expand Down
5 changes: 2 additions & 3 deletions HARK/ConsumptionSaving/ConsMarkovModel.py
Original file line number Diff line number Diff line change
Expand Up @@ -1182,10 +1182,9 @@ def make_vFunc(self, solution):
vNvrsP = np.insert(
vNvrsP, 0, self.MPCmaxEff[i] ** (-self.CRRA / (1.0 - self.CRRA))
)
MPCminNvrs = self.MPCminNow[i] ** (-self.CRRA / (1.0 - self.CRRA))
#MPCminNvrs = self.MPCminNow[i] ** (-self.CRRA / (1.0 - self.CRRA))
vNvrsFunc_i = CubicInterp(
mNrm_temp, vNvrs, vNvrsP, MPCminNvrs * self.hNrmNow[i], MPCminNvrs
)
mNrm_temp, vNvrs, vNvrsP,) # MPCminNvrs * self.hNrmNow[i], MPCminNvrs

# "Recurve" the decurved value function and add it to the list
vFunc_i = ValueFuncCRRA(vNvrsFunc_i, self.CRRA)
Expand Down

0 comments on commit 056cbd6

Please sign in to comment.