Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make non-OO consumption-saving solvers #1394

Merged
merged 58 commits into from
Mar 27, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
35c63b1
Add simplified PF solver
mnwhite Mar 5, 2024
3ed2e45
Add simplified based ConsIndShockSolver
mnwhite Mar 5, 2024
a3b3c78
Add cubic option to simplified ConsIndShock solver
mnwhite Mar 6, 2024
a3f3a6c
Add vFuncBool capability to simplified solver
mnwhite Mar 6, 2024
34b7555
Run black on simplified solver
mnwhite Mar 6, 2024
3d92dd1
Add simplified ConsKinkedR solver
mnwhite Mar 6, 2024
ab4b086
Run black again
mnwhite Mar 6, 2024
8faf1a1
Fix error in MPCmax in KinkedR
mnwhite Mar 6, 2024
8c4e060
Actually fix error properly
mnwhite Mar 6, 2024
f245acb
Discrepancy actually due to indexing offset error
mnwhite Mar 7, 2024
747eeab
Made basic ConsPortfolio simplified solver
mnwhite Mar 8, 2024
45d645c
Add vFunc capability to simplified portfolio solver
mnwhite Mar 8, 2024
d9cda03
Run black on ConsPortfolioModel.py
mnwhite Mar 8, 2024
830764b
Add discrete choice capability to simplified portfolio solver
mnwhite Mar 11, 2024
264186a
Run black, move import line
mnwhite Mar 11, 2024
936930a
Merge branch 'master' into SimplifySolvers
mnwhite Mar 11, 2024
fb10736
Add simple solver for ConsPrefShock
mnwhite Mar 11, 2024
f592a98
More formatting nonsense
mnwhite Mar 11, 2024
0dd9ab3
Make simple KinkyPrefShock solver
mnwhite Mar 11, 2024
5c6a5a5
Actually use new KinkyPref solver
mnwhite Mar 11, 2024
491a6f6
Make simple solver for ConsMarkovModel
mnwhite Mar 12, 2024
8d3cc8d
Fix formatting error and cubic interpolation typo
mnwhite Mar 12, 2024
00861e6
Added vFunc capability to simple ConsMarkov solver
mnwhite Mar 13, 2024
c6f8fc1
Resolve vFunc bug by suppressing extrapolation limit
mnwhite Mar 13, 2024
ea15b11
Delete two blank lines that upset ruff
mnwhite Mar 13, 2024
056cbd6
Add simple solver for WarmGlowBequest
mnwhite Mar 13, 2024
1568878
Add simple solver for WarmGlowPortfolioModel
mnwhite Mar 13, 2024
753df28
Actually use new solver
mnwhite Mar 13, 2024
ccbdd76
Missed one unneeded import
mnwhite Mar 13, 2024
f33f16d
Add simple solver for ConsGenIncProcess
mnwhite Mar 14, 2024
9e014f0
Remove unneeded imports
mnwhite Mar 14, 2024
8b759b4
First version of simple ConsRiskyAsset solver
mnwhite Mar 15, 2024
0a94ca7
Value function works for ConsRiskyAsset
mnwhite Mar 15, 2024
28afed2
Add independent distribution capability
mnwhite Mar 15, 2024
1c3b683
Fixed Cubic interpolation issue
mnwhite Mar 15, 2024
7b4da9c
Forgot to re-activate new solver
mnwhite Mar 16, 2024
bbdc412
Add simple solver for ConsMedShockModel
mnwhite Mar 18, 2024
f275116
Add vFunc and cubic capability to WarmGlowBequest
mnwhite Mar 20, 2024
a4105a3
Non-independent distributions for ConsPortfolioModel
mnwhite Mar 20, 2024
cf41a10
I guess I mistakenly deleted a carriage return?
mnwhite Mar 20, 2024
260a3f1
Now there was an extra tab
mnwhite Mar 20, 2024
ccf9c8a
Update CHANGELOG
mnwhite Mar 22, 2024
561872e
Begin moving OO solvers out of model files
mnwhite Mar 22, 2024
6f9fc02
Move more solvers, try to resolve some testing issues
mnwhite Mar 22, 2024
20352e6
Even more solvers moved, trying to resolve test conflicts
mnwhite Mar 22, 2024
715f845
Move more legacy code, fix more imports
mnwhite Mar 23, 2024
c0f76bb
Fix IndShockFast imports
mnwhite Mar 23, 2024
e2c38fb
Remove ConsPortfolioJointDistSolver from tests
mnwhite Mar 23, 2024
964894c
Add simple solver for alternate ConsPortfolio
mnwhite Mar 25, 2024
eb96ab7
Merge branch 'master' into SimplifySolvers
mnwhite Mar 25, 2024
736b424
Formatting fixes
mnwhite Mar 25, 2024
765be38
Two tiny formatting fixes
mnwhite Mar 25, 2024
c1210e2
And again
mnwhite Mar 25, 2024
27c01ab
Restore fixed share type to ConsRiskyAssetModel
mnwhite Mar 25, 2024
4e0436c
Change import location
mnwhite Mar 25, 2024
428ddd5
Even more formatting
mnwhite Mar 25, 2024
18c8f70
Lint loves linebreaks
mnwhite Mar 25, 2024
b4583a8
Update example notebooks
mnwhite Mar 25, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
379 changes: 379 additions & 0 deletions HARK/ConsumptionSaving/ConsPortfolioModel.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
ValueFuncCRRA,
)
from HARK.metric import MetricObject
from HARK.rewards import UtilityFuncCRRA
from HARK.distribution import expected


# Define a class to represent the single period solution of the portfolio choice problem
Expand Down Expand Up @@ -180,6 +182,7 @@
else:
solver = ConsPortfolioJointDistSolver
self.solve_one_period = make_one_period_oo_solver(solver)
#self.solve_one_period = solve_one_period_ConsPortfolio

self.update()

Expand Down Expand Up @@ -328,6 +331,382 @@

# Set the solver for the portfolio model, and update various constructed attributes
self.solve_one_period = make_one_period_oo_solver(ConsSequentialPortfolioSolver)


def solve_one_period_ConsPortfolio( solution_next,
ShockDstn,
IncShkDstn,
RiskyDstn,
LivPrb,
DiscFac,
CRRA,
Rfree,
PermGroFac,
BoroCnstArt,
aXtraGrid,
ShareGrid,
AdjustPrb,
ShareLimit,
vFuncBool,
DiscreteShareBool,
IndepDstnBool,):
"""
Solve one period of a consumption-saving problem with portfolio allocation
between a riskless and risky asset. This function handles various sub-cases
or variations on the problem, including the possibility that the agent does
not necessarily get to update their portfolio share in every period, or that
they must choose a discrete rather than continuous risky share.

Parameters
----------
solution_next : PortfolioSolution
Solution to next period's problem.
ShockDstn : Distribution
Joint distribution of permanent income shocks, transitory income shocks,
and risky returns. This is only used if the input IndepDstnBool is False,
indicating that income and return distributions can't be assumed to be
independent.
IncShkDstn : Distribution
Discrete distribution of permanent income shocks and transitory income
shocks. This is only used if the input IndepDstnBool is True, indicating
that income and return distributions are independent.
RiskyDstn : Distribution
Distribution of risky asset returns. This is only used if the input
IndepDstnBool is True, indicating that income and return distributions
are independent.
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. In this model, it is *required* to be zero.
aXtraGrid: np.array
Array of "extra" end-of-period asset values-- assets above the
absolute minimum acceptable level.
ShareGrid : np.array
Array of risky portfolio shares on which to define the interpolation
of the consumption function when Share is fixed. Also used when the
risky share choice is specified as discrete rather than continuous.
AdjustPrb : float
Probability that the agent will be able to update his portfolio share.
ShareLimit : float
Limiting lower bound of risky portfolio share as mNrm approaches infinity.
vFuncBool: boolean
An indicator for whether the value function should be computed and
included in the reported solution.
DiscreteShareBool : bool
Indicator for whether risky portfolio share should be optimized on the
continuous [0,1] interval using the FOC (False), or instead only selected
from the discrete set of values in ShareGrid (True). If True, then
vFuncBool must also be True.
IndepDstnBool : bool
Indicator for whether the income and risky return distributions are in-
dependent of each other, which can speed up the expectations step.

Returns
-------
solution_now : PortfolioSolution
Solution to this period's problem.
"""
# Make sure the individual is liquidity constrained. Allowing a consumer to
# borrow *and* invest in an asset with unbounded (negative) returns is a bad mix.
if BoroCnstArt != 0.0:
raise ValueError("PortfolioConsumerType must have BoroCnstArt=0.0!")

Check warning on line 422 in HARK/ConsumptionSaving/ConsPortfolioModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsPortfolioModel.py#L421-L422

Added lines #L421 - L422 were not covered by tests

# Make sure that if risky portfolio share is optimized only discretely, then
# the value function is also constructed (else this task would be impossible).
if DiscreteShareBool and (not vFuncBool):
raise ValueError(

Check warning on line 427 in HARK/ConsumptionSaving/ConsPortfolioModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsPortfolioModel.py#L426-L427

Added lines #L426 - L427 were not covered by tests
"PortfolioConsumerType requires vFuncBool to be True when DiscreteShareBool is True!"
)

# Define the current period utility function and effective discount factor
uFunc = UtilityFuncCRRA(CRRA)
DiscFacEff = DiscFac * LivPrb # "effective" discount factor

Check warning on line 433 in HARK/ConsumptionSaving/ConsPortfolioModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsPortfolioModel.py#L432-L433

Added lines #L432 - L433 were not covered by tests

# Unpack next period's solution for easier access
vPfuncAdj_next = solution_next.vPfuncAdj
dvdmFuncFxd_next = solution_next.dvdmFuncFxd
dvdsFuncFxd_next = solution_next.dvdsFuncFxd
vFuncAdj_next = solution_next.vFuncAdj
vFuncFxd_next = solution_next.vFuncFxd

Check warning on line 440 in HARK/ConsumptionSaving/ConsPortfolioModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsPortfolioModel.py#L436-L440

Added lines #L436 - L440 were not covered by tests

# Set a flag for whether the natural borrowing constraint is zero, which
# depends on whether the smallest transitory income shock is zero
BoroCnstNat_iszero = np.min(IncShkDstn.atoms[1]) == 0.0

Check warning on line 444 in HARK/ConsumptionSaving/ConsPortfolioModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsPortfolioModel.py#L444

Added line #L444 was not covered by tests

# Prepare to calculate end-of-period marginal values by creating an array
# of market resources that the agent could have next period, considering
# the grid of end-of-period assets and the distribution of shocks he might
# experience next period.

# Unpack the risky return shock distribution
Risky_next = RiskyDstn.atoms
RiskyMax = np.max(Risky_next)
RiskyMin = np.min(Risky_next)

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

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsPortfolioModel.py#L452-L454

Added lines #L452 - L454 were not covered by tests

# bNrm represents R*a, balances after asset return shocks but before income.
# This just uses the highest risky return as a rough shifter for the aXtraGrid.
if BoroCnstNat_iszero:
aNrmGrid = aXtraGrid
bNrmGrid = np.insert(RiskyMax * aXtraGrid, 0, RiskyMin * aXtraGrid[0])

Check warning on line 460 in HARK/ConsumptionSaving/ConsPortfolioModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsPortfolioModel.py#L458-L460

Added lines #L458 - L460 were not covered by tests
else:
# Add an asset point at exactly zero
aNrmGrid = np.insert(aXtraGrid, 0, 0.0)
bNrmGrid = RiskyMax * np.insert(aXtraGrid, 0, 0.0)

Check warning on line 464 in HARK/ConsumptionSaving/ConsPortfolioModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsPortfolioModel.py#L463-L464

Added lines #L463 - L464 were not covered by tests

# Get grid and shock sizes, for easier indexing
aNrmCount = aNrmGrid.size
ShareCount = ShareGrid.size

Check warning on line 468 in HARK/ConsumptionSaving/ConsPortfolioModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsPortfolioModel.py#L467-L468

Added lines #L467 - L468 were not covered by tests

# Make tiled arrays to calculate future realizations of mNrm and Share when integrating over IncShkDstn
bNrmNext, ShareNext = np.meshgrid(bNrmGrid, ShareGrid, indexing="ij")

Check warning on line 471 in HARK/ConsumptionSaving/ConsPortfolioModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsPortfolioModel.py#L471

Added line #L471 was not covered by tests

# Define functions that are used internally to evaluate future realizations
def calc_mNrm_next(S, b):

Check warning on line 474 in HARK/ConsumptionSaving/ConsPortfolioModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsPortfolioModel.py#L474

Added line #L474 was not covered by tests
"""
Calculate future realizations of market resources mNrm from the income
shock distribution S and normalized bank balances b.
"""
return b / (S["PermShk"] * PermGroFac) + S["TranShk"]

Check warning on line 479 in HARK/ConsumptionSaving/ConsPortfolioModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsPortfolioModel.py#L479

Added line #L479 was not covered by tests

def calc_dvdm_next(S, b, z):

Check warning on line 481 in HARK/ConsumptionSaving/ConsPortfolioModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsPortfolioModel.py#L481

Added line #L481 was not covered by tests
"""
Evaluate realizations of marginal value of market resources next period,
based on the income distribution S, values of bank balances bNrm, and
values of the risky share z.
"""
mNrm_next = calc_mNrm_next(S, b)
dvdmAdj_next = vPfuncAdj_next(mNrm_next)

Check warning on line 488 in HARK/ConsumptionSaving/ConsPortfolioModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsPortfolioModel.py#L487-L488

Added lines #L487 - L488 were not covered by tests

if AdjustPrb < 1.0:

Check warning on line 490 in HARK/ConsumptionSaving/ConsPortfolioModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsPortfolioModel.py#L490

Added line #L490 was not covered by tests
# Expand to the same dimensions as mNrm
Share_next_expanded = z + np.zeros_like(mNrm_next)
dvdmFxd_next = dvdmFuncFxd_next(mNrm_next, Share_next_expanded)

Check warning on line 493 in HARK/ConsumptionSaving/ConsPortfolioModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsPortfolioModel.py#L492-L493

Added lines #L492 - L493 were not covered by tests
# Combine by adjustment probability
dvdm_next = AdjustPrb * dvdmAdj_next + (1.0 - AdjustPrb) * dvdmFxd_next

Check warning on line 495 in HARK/ConsumptionSaving/ConsPortfolioModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsPortfolioModel.py#L495

Added line #L495 was not covered by tests
else: # Don't bother evaluating if there's no chance that portfolio share is fixed
dvdm_next = dvdmAdj_next

Check warning on line 497 in HARK/ConsumptionSaving/ConsPortfolioModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsPortfolioModel.py#L497

Added line #L497 was not covered by tests

dvdm_next = (S["PermShk"] * PermGroFac) ** (-CRRA) * dvdm_next
return dvdm_next

Check warning on line 500 in HARK/ConsumptionSaving/ConsPortfolioModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsPortfolioModel.py#L499-L500

Added lines #L499 - L500 were not covered by tests

def calc_dvds_next(S, b, z):

Check warning on line 502 in HARK/ConsumptionSaving/ConsPortfolioModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsPortfolioModel.py#L502

Added line #L502 was not covered by tests
"""
Evaluate realizations of marginal value of risky share next period, based
on the income distribution S, values of bank balances bNrm, and values of
the risky share z.
"""
mNrm_next = calc_mNrm_next(S, b)

Check warning on line 508 in HARK/ConsumptionSaving/ConsPortfolioModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsPortfolioModel.py#L508

Added line #L508 was not covered by tests

# No marginal value of Share if it's a free choice!
dvdsAdj_next = np.zeros_like(mNrm_next)

Check warning on line 511 in HARK/ConsumptionSaving/ConsPortfolioModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsPortfolioModel.py#L511

Added line #L511 was not covered by tests

if AdjustPrb < 1.0:

Check warning on line 513 in HARK/ConsumptionSaving/ConsPortfolioModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsPortfolioModel.py#L513

Added line #L513 was not covered by tests
# Expand to the same dimensions as mNrm
Share_next_expanded = z + np.zeros_like(mNrm_next)
dvdsFxd_next = dvdsFuncFxd_next(mNrm_next, Share_next_expanded)

Check warning on line 516 in HARK/ConsumptionSaving/ConsPortfolioModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsPortfolioModel.py#L515-L516

Added lines #L515 - L516 were not covered by tests
# Combine by adjustment probability
dvds_next = AdjustPrb * dvdsAdj_next + (1.0 - AdjustPrb) * dvdsFxd_next

Check warning on line 518 in HARK/ConsumptionSaving/ConsPortfolioModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsPortfolioModel.py#L518

Added line #L518 was not covered by tests
else: # Don't bother evaluating if there's no chance that portfolio share is fixed
dvds_next = dvdsAdj_next

Check warning on line 520 in HARK/ConsumptionSaving/ConsPortfolioModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsPortfolioModel.py#L520

Added line #L520 was not covered by tests

dvds_next = (S["PermShk"] * PermGroFac) ** (1.0 - CRRA) * dvds_next
return dvds_next

Check warning on line 523 in HARK/ConsumptionSaving/ConsPortfolioModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsPortfolioModel.py#L522-L523

Added lines #L522 - L523 were not covered by tests

# Calculate end-of-period marginal value of assets and shares at each point
# in aNrm and ShareGrid. Does so by taking expectation of next period marginal
# values across income and risky return shocks.

# Calculate intermediate marginal value of bank balances by taking expectations over income shocks
dvdb_intermed = expected(calc_dvdm_next, IncShkDstn, args=(bNrmNext, ShareNext))
dvdbNvrs_intermed = uFunc.derinv(dvdb_intermed, order=(1, 0))
dvdbNvrsFunc_intermed = BilinearInterp(dvdbNvrs_intermed, bNrmGrid, ShareGrid)
dvdbFunc_intermed = MargValueFuncCRRA(dvdbNvrsFunc_intermed, CRRA)

Check warning on line 533 in HARK/ConsumptionSaving/ConsPortfolioModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsPortfolioModel.py#L530-L533

Added lines #L530 - L533 were not covered by tests

# Calculate intermediate marginal value of risky portfolio share by taking expectations over income shocks
dvds_intermed = expected(calc_dvds_next, IncShkDstn, args=(bNrmNext, ShareNext))
dvdsFunc_intermed = BilinearInterp(dvds_intermed, bNrmGrid, ShareGrid)

Check warning on line 537 in HARK/ConsumptionSaving/ConsPortfolioModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsPortfolioModel.py#L536-L537

Added lines #L536 - L537 were not covered by tests

# Make tiled arrays to calculate future realizations of bNrm and Share when integrating over RiskyDstn
aNrmNow, ShareNext = np.meshgrid(aNrmGrid, ShareGrid, indexing="ij")

Check warning on line 540 in HARK/ConsumptionSaving/ConsPortfolioModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsPortfolioModel.py#L540

Added line #L540 was not covered by tests

# Define functions for calculating end-of-period marginal value
def calc_EndOfPrd_dvda(S, a, z):

Check warning on line 543 in HARK/ConsumptionSaving/ConsPortfolioModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsPortfolioModel.py#L543

Added line #L543 was not covered by tests
'''
Compute end-of-period marginal value of assets at values a, conditional
on risky asset return S and risky share z.
'''
# Calculate future realizations of bank balances bNrm
Rxs = S - Rfree # Excess returns
Rport = Rfree + z * Rxs # Portfolio return
bNrm_next = Rport * a

Check warning on line 551 in HARK/ConsumptionSaving/ConsPortfolioModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsPortfolioModel.py#L549-L551

Added lines #L549 - L551 were not covered by tests

# Ensure shape concordance
z_rep = z + np.zeros_like(bNrm_next)

Check warning on line 554 in HARK/ConsumptionSaving/ConsPortfolioModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsPortfolioModel.py#L554

Added line #L554 was not covered by tests

# Calculate and return dvda
EndOfPrd_dvda = Rport * dvdbFunc_intermed(bNrm_next, z_rep)
return EndOfPrd_dvda

Check warning on line 558 in HARK/ConsumptionSaving/ConsPortfolioModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsPortfolioModel.py#L557-L558

Added lines #L557 - L558 were not covered by tests

def EndOfPrddvds_dist(S, a, z):

Check warning on line 560 in HARK/ConsumptionSaving/ConsPortfolioModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsPortfolioModel.py#L560

Added line #L560 was not covered by tests
'''
Compute end-of-period marginal value of risky share at values a, conditional
on risky asset return S and risky share z.
'''
# Calculate future realizations of bank balances bNrm
Rxs = S - Rfree # Excess returns
Rport = Rfree + z * Rxs # Portfolio return
bNrm_next = Rport * a

Check warning on line 568 in HARK/ConsumptionSaving/ConsPortfolioModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsPortfolioModel.py#L566-L568

Added lines #L566 - L568 were not covered by tests

# Make the shares match the dimension of b, so that it can be vectorized
z_rep = z + np.zeros_like(bNrm_next)

Check warning on line 571 in HARK/ConsumptionSaving/ConsPortfolioModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsPortfolioModel.py#L571

Added line #L571 was not covered by tests

# Calculate and return dvds
EndOfPrd_dvds = Rxs * a * dvdbFunc_intermed(bNrm_next, z_rep) + dvdsFunc_intermed(bNrm_next, z_rep)
return EndOfPrd_dvds

Check warning on line 575 in HARK/ConsumptionSaving/ConsPortfolioModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsPortfolioModel.py#L574-L575

Added lines #L574 - L575 were not covered by tests

# Evaluate realizations of value and marginal value after asset returns are realized

# Calculate end-of-period marginal value of assets by taking expectations
EndOfPrd_dvda = DiscFacEff * expected(calc_EndOfPrd_dvda, RiskyDstn, args=(aNrmNow, ShareNext))
EndOfPrd_dvdaNvrs = uFunc.derinv(EndOfPrd_dvda)

Check warning on line 581 in HARK/ConsumptionSaving/ConsPortfolioModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsPortfolioModel.py#L580-L581

Added lines #L580 - L581 were not covered by tests

# Calculate end-of-period marginal value of risky portfolio share by taking expectations
EndOfPrd_dvds = DiscFacEff * expected(EndOfPrddvds_dist, RiskyDstn, args=(aNrmNow, ShareNext))

Check warning on line 584 in HARK/ConsumptionSaving/ConsPortfolioModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsPortfolioModel.py#L584

Added line #L584 was not covered by tests

# Now find the optimal (continuous) risky share on [0,1] by solving the first
# order condition EndOfPrd_dvds == 0.
FOC_s = EndOfPrd_dvds # Relabel for convenient typing

Check warning on line 588 in HARK/ConsumptionSaving/ConsPortfolioModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsPortfolioModel.py#L588

Added line #L588 was not covered by tests

# For each value of aNrm, find the value of Share such that FOC_s == 0
crossing = np.logical_and(FOC_s[:, 1:] <= 0.0, FOC_s[:, :-1] >= 0.0)
share_idx = np.argmax(crossing, axis=1)

Check warning on line 592 in HARK/ConsumptionSaving/ConsPortfolioModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsPortfolioModel.py#L591-L592

Added lines #L591 - L592 were not covered by tests
# This represents the index of the segment of the share grid where dvds flips
# from positive to negative, indicating that there's a zero *on* the segment

# Calculate the fractional distance between those share gridpoints where the
# zero should be found, assuming a linear function; call it alpha
a_idx = np.arange(aNrmCount)
bot_s = ShareGrid[share_idx]
top_s = ShareGrid[share_idx + 1]
bot_f = FOC_s[a_idx, share_idx]
top_f = FOC_s[a_idx, share_idx + 1]
bot_c = EndOfPrd_dvdaNvrs[a_idx, share_idx]
top_c = EndOfPrd_dvdaNvrs[a_idx, share_idx + 1]
alpha = 1.0 - top_f / (top_f - bot_f)

Check warning on line 605 in HARK/ConsumptionSaving/ConsPortfolioModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsPortfolioModel.py#L598-L605

Added lines #L598 - L605 were not covered by tests

# Calculate the continuous optimal risky share and optimal consumption
ShareAdj_now = (1.0 - alpha) * bot_s + alpha * top_s
cNrmAdj_now = (1.0 - alpha) * bot_c + alpha * top_c

Check warning on line 609 in HARK/ConsumptionSaving/ConsPortfolioModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsPortfolioModel.py#L608-L609

Added lines #L608 - L609 were not covered by tests

# If agent wants to put more than 100% into risky asset, he is constrained.
# Likewise if he wants to put less than 0% into risky asset, he is constrained.
constrained_top = FOC_s[:, -1] > 0.0
constrained_bot = FOC_s[:, 0] < 0.0

Check warning on line 614 in HARK/ConsumptionSaving/ConsPortfolioModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsPortfolioModel.py#L613-L614

Added lines #L613 - L614 were not covered by tests

# Apply those constraints to both risky share and consumption (but lower
# constraint should never be relevant)
ShareAdj_now[constrained_top] = 1.0
ShareAdj_now[constrained_bot] = 0.0
cNrmAdj_now[constrained_top] = EndOfPrd_dvdaNvrs[constrained_top, -1]
cNrmAdj_now[constrained_bot] = EndOfPrd_dvdaNvrs[constrained_bot, 0]

Check warning on line 621 in HARK/ConsumptionSaving/ConsPortfolioModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsPortfolioModel.py#L618-L621

Added lines #L618 - L621 were not covered by tests

# When the natural borrowing constraint is *not* zero, then aNrm=0 is in the
# grid, but there's no way to "optimize" the portfolio if a=0, and consumption
# can't depend on the risky share if it doesn't meaningfully exist. Apply
# a small fix to the bottom gridpoint (aNrm=0) when this happens.
if (not BoroCnstNat_iszero):
ShareAdj_now[0] = 1.0
cNrmAdj_now[0] = EndOfPrd_dvdaNvrs[0, -1]

Check warning on line 629 in HARK/ConsumptionSaving/ConsPortfolioModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsPortfolioModel.py#L627-L629

Added lines #L627 - L629 were not covered by tests

# Construct functions characterizing the solution for this period

# Calculate the endogenous mNrm gridpoints when the agent adjusts his portfolio,
# then construct the consumption function when the agent can adjust his share
mNrmAdj_now = np.insert(aNrmGrid + cNrmAdj_now, 0, 0.0)
cNrmAdj_now = np.insert(cNrmAdj_now, 0, 0.0)
cFuncAdj_now = LinearInterp(mNrmAdj_now, cNrmAdj_now)

Check warning on line 637 in HARK/ConsumptionSaving/ConsPortfolioModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsPortfolioModel.py#L635-L637

Added lines #L635 - L637 were not covered by tests

# Construct the marginal value (of mNrm) function when the agent can adjust
vPfuncAdj_now = MargValueFuncCRRA(cFuncAdj_now, CRRA)

Check warning on line 640 in HARK/ConsumptionSaving/ConsPortfolioModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsPortfolioModel.py#L640

Added line #L640 was not covered by tests

# Construct the consumption function when the agent *can't* adjust the risky
# share, as well as the marginal value of Share function
cFuncFxd_by_Share = []
dvdsFuncFxd_by_Share = []
for j in range(ShareCount):
cNrmFxd_temp = np.insert(EndOfPrd_dvdaNvrs[:, j], 0, 0.0)
mNrmFxd_temp = np.insert(aNrmGrid + cNrmFxd_temp[1:], 0, 0.0)
dvdsFxd_temp = np.insert(EndOfPrd_dvds[:, j], 0, EndOfPrd_dvds[0, j])
cFuncFxd_by_Share.append(LinearInterp(mNrmFxd_temp, cNrmFxd_temp))
dvdsFuncFxd_by_Share.append(LinearInterp(mNrmFxd_temp, dvdsFxd_temp))
cFuncFxd_now = LinearInterpOnInterp1D(cFuncFxd_by_Share, ShareGrid)
dvdsFuncFxd_now = LinearInterpOnInterp1D(dvdsFuncFxd_by_Share, ShareGrid)

Check warning on line 653 in HARK/ConsumptionSaving/ConsPortfolioModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsPortfolioModel.py#L644-L653

Added lines #L644 - L653 were not covered by tests

# The share function when the agent can't adjust his portfolio is trivial
ShareFuncFxd_now = IdentityFunction(i_dim=1, n_dims=2)

Check warning on line 656 in HARK/ConsumptionSaving/ConsPortfolioModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsPortfolioModel.py#L656

Added line #L656 was not covered by tests

# Construct the marginal value of mNrm function when the agent can't adjust his share
dvdmFuncFxd_now = MargValueFuncCRRA(cFuncFxd_now, CRRA)

Check warning on line 659 in HARK/ConsumptionSaving/ConsPortfolioModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsPortfolioModel.py#L659

Added line #L659 was not covered by tests

# Construct the optimal risky share function when adjusting is possible
if BoroCnstNat_iszero:
Share_lower_bound = ShareLimit

Check warning on line 663 in HARK/ConsumptionSaving/ConsPortfolioModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsPortfolioModel.py#L662-L663

Added lines #L662 - L663 were not covered by tests
else:
Share_lower_bound = 1.0
ShareAdj_now = np.insert(ShareAdj_now, 0, Share_lower_bound)
ShareFuncAdj_now = LinearInterp(mNrmAdj_now, ShareAdj_now, ShareLimit, 0.0)

Check warning on line 667 in HARK/ConsumptionSaving/ConsPortfolioModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsPortfolioModel.py#L665-L667

Added lines #L665 - L667 were not covered by tests

# This is a point at which (a,c,share) have consistent length. Take the
# snapshot for storing the grid and values in the solution.
save_points = {

Check warning on line 671 in HARK/ConsumptionSaving/ConsPortfolioModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsPortfolioModel.py#L671

Added line #L671 was not covered by tests
"a": deepcopy(aNrmGrid),
"eop_dvda_adj": uFunc.der(cNrmAdj_now),
"share_adj": deepcopy(ShareAdj_now),
"share_grid": deepcopy(ShareGrid),
"eop_dvda_fxd": uFunc.der(EndOfPrd_dvda),
"eop_dvds_fxd": EndOfPrd_dvds,
}

# Add the value function if requested TODO
if vFuncBool:
vFuncAdj_now = NullFunc()
vFuncFxd_now = NullFunc()

Check warning on line 683 in HARK/ConsumptionSaving/ConsPortfolioModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsPortfolioModel.py#L681-L683

Added lines #L681 - L683 were not covered by tests
else: # If vFuncBool is False, fill in dummy values
vFuncAdj_now = NullFunc()
vFuncFxd_now = NullFunc()

Check warning on line 686 in HARK/ConsumptionSaving/ConsPortfolioModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsPortfolioModel.py#L685-L686

Added lines #L685 - L686 were not covered by tests

# Package and return the solution
solution_now = PortfolioSolution(

Check warning on line 689 in HARK/ConsumptionSaving/ConsPortfolioModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsPortfolioModel.py#L689

Added line #L689 was not covered by tests
cFuncAdj=cFuncAdj_now,
ShareFuncAdj=ShareFuncAdj_now,
vPfuncAdj=vPfuncAdj_now,
vFuncAdj=vFuncAdj_now,
cFuncFxd=cFuncFxd_now,
ShareFuncFxd=ShareFuncFxd_now,
dvdmFuncFxd=dvdmFuncFxd_now,
dvdsFuncFxd=dvdsFuncFxd_now,
vFuncFxd=vFuncFxd_now,
AdjPrb=AdjustPrb,
# WHAT IS THIS STUFF FOR??
aGrid=save_points["a"],
Share_adj=save_points["share_adj"],
EndOfPrddvda_adj=save_points["eop_dvda_adj"],
ShareGrid=save_points["share_grid"],
EndOfPrddvda_fxd=save_points["eop_dvda_fxd"],
EndOfPrddvds_fxd=save_points["eop_dvds_fxd"],
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think at some point we were saving some midway calculations for diagnosing issues?

We should probably remove these, unless they are somehow important downstream for @Mv77


)
return solution_now

Check warning on line 709 in HARK/ConsumptionSaving/ConsPortfolioModel.py

View check run for this annotation

Codecov / codecov/patch

HARK/ConsumptionSaving/ConsPortfolioModel.py#L709

Added line #L709 was not covered by tests


class ConsPortfolioSolver(MetricObject):
Expand Down
Loading
Loading