Skip to content

Update Jacobian Methods For ConsMarkovType and IndShockConsumerType [WIP]#1248

Open
wdu9 wants to merge 7 commits intoecon-ark:mainfrom
wdu9:Update_JAC
Open

Update Jacobian Methods For ConsMarkovType and IndShockConsumerType [WIP]#1248
wdu9 wants to merge 7 commits intoecon-ark:mainfrom
wdu9:Update_JAC

Conversation

@wdu9
Copy link
Collaborator

@wdu9 wdu9 commented Mar 25, 2023

Please ensure your pull request adheres to the following guidelines:

  • Tests for new functionality/models or Tests to reproduce the bug-fix in code.
  • Updated documentation of features that add new functionality.
  • Update CHANGELOG.md with major/minor changes.

@codecov-commenter
Copy link

codecov-commenter commented Mar 25, 2023

Codecov Report

Patch coverage: 4.87% and project coverage change: -1.11 ⚠️

Comparison is base (80d027d) 73.31% compared to head (63e3daf) 72.20%.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #1248      +/-   ##
==========================================
- Coverage   73.31%   72.20%   -1.11%     
==========================================
  Files          76       76              
  Lines       12548    12751     +203     
==========================================
+ Hits         9199     9207       +8     
- Misses       3349     3544     +195     
Impacted Files Coverage Δ
HARK/utilities.py 20.51% <1.40%> (-3.85%) ⬇️
HARK/ConsumptionSaving/ConsMarkovModel.py 63.52% <6.71%> (-22.28%) ⬇️

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

☔ View full report in Codecov by Sentry.
📢 Do you have feedback about the report comment? Let us know in this issue.

@review-notebook-app
Copy link

Check out this pull request on  ReviewNB

See visual diffs & provide feedback on Jupyter Notebooks.


Powered by ReviewNB

@codecov
Copy link

codecov bot commented Jul 6, 2023

Codecov Report

Patch coverage: 4.87% and project coverage change: -1.06 ⚠️

Comparison is base (37134b9) 72.55% compared to head (9c8e698) 71.50%.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #1248      +/-   ##
==========================================
- Coverage   72.55%   71.50%   -1.06%     
==========================================
  Files          78       78              
  Lines       13009    13212     +203     
==========================================
+ Hits         9439     9447       +8     
- Misses       3570     3765     +195     
Impacted Files Coverage Δ
HARK/utilities.py 20.51% <1.40%> (-3.85%) ⬇️
HARK/ConsumptionSaving/ConsMarkovModel.py 63.52% <6.71%> (-22.28%) ⬇️

☔ View full report in Codecov by Sentry.
📢 Do you have feedback about the report comment? Let us know in this issue.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR introduces sequence-space Jacobian utilities and extends MarkovConsumerType with transition-matrix and steady-state calculations, alongside relaxing shape checks for Markov-specific interest rate inputs.

Changes:

  • Added Fake_News_JAC in HARK/utilities.py to compute asset and consumption Jacobians using the “fake news” algorithm, factoring out logic previously in IndShockConsumerType.calc_jacobian.
  • Extended MarkovConsumerType with calc_transition_matrix, calc_ergodic_dist, and compute_steady_state, plus new imports for transition-matrix utilities and sparse eigen computations.
  • Relaxed MarkovConsumerType.check_markov_inputs to allow Rfree to be provided as a list of arrays as well as a single NumPy array.

Reviewed changes

Copilot reviewed 2 out of 3 changed files in this pull request and generated 18 comments.

File Description
HARK/utilities.py Adds the Fake_News_JAC helper implementing the fake-news Jacobian algorithm in a reusable form (with some docstring issues to fix).
HARK/ConsumptionSaving/ConsMarkovModel.py Introduces Markov-specific transition-matrix, ergodic distribution, and steady-state computation logic and broadens Rfree validation, but contains several shape, indexing, and API-consistency problems that need correction and tests.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +1305 to +1307


LivPrb = self.LivPrb[0][0] # Update probability of staying alive
Copy link

Copilot AI Jan 28, 2026

Choose a reason for hiding this comment

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

In the infinite-horizon branch, the survival probability used in the transition matrices is hard-coded as self.LivPrb[0][0], i.e., the survival probability of the first Markov state only. Since self.LivPrb is validated above as a vector of length StateCount, this effectively ignores any heterogeneity in survival probabilities across Markov states; consider indexing by i when constructing the transition matrix if state-specific survival is intended.

Copilot uses AI. Check for mistakes.
Comment on lines +1493 to +1518
def compute_steady_state(self,IncShkDstn_ntrl_msr):




#solve the consumer's problem

self.solve()

self.define_distribution_grid(dist_pGrid = np.array([1]))
self.calc_transition_matrix(IncShkDstn_ntrl_msr)
self.calc_ergodic_dist()


C = 0
A = 0
for i in range(self.state_num):

C += self.prb_dstn[i]*np.dot(self.cPol_Grid[i],self.vec_erg_dstns[i])
A += self.prb_dstn[i]*np.dot(self.aPol_Grid[i],self.vec_erg_dstns[i])


self.A_ss = A
self.C_ss = C

return C , A
Copy link

Copilot AI Jan 28, 2026

Choose a reason for hiding this comment

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

compute_steady_state for MarkovConsumerType returns (C, A) while the analogous compute_steady_state method for IndShockConsumerType returns (A_ss, C_ss). This inconsistency in return order between closely related types makes it easy to misinterpret results when swapping models; consider standardizing the return tuple (and documenting it) so both classes follow the same convention.

Copilot uses AI. Check for mistakes.
Parameters
----------
transition_matrix: List
transition matrix whose ergordic distribution is to be solved
Copy link

Copilot AI Jan 28, 2026

Choose a reason for hiding this comment

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

The docstring for calc_ergodic_dist describes the parameter as a "transition matrix whose ergordic distribution is to be solved"; "ergordic" is misspelled and should be "ergodic".

Suggested change
transition matrix whose ergordic distribution is to be solved
transition matrix whose ergodic distribution is to be solved

Copilot uses AI. Check for mistakes.
Comment on lines +882 to +897
if isinstance(self.Rfree,list):

for rfree_t in self.Rfree:

# Check that arrays are the right shape
if not isinstance(rfree_t, np.ndarray) or rfree_t.shape != (StateCount,):
raise ValueError(
"Rfree not the right shape, it should an array of Rfree of all the states."
)
else:

# Check that arrays are the right shape
if not isinstance(self.Rfree, np.ndarray) or self.Rfree.shape != (StateCount,):
raise ValueError(
"Rfree not the right shape, it should an array of Rfree of all the states."
)
Copy link

Copilot AI Jan 28, 2026

Choose a reason for hiding this comment

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

check_markov_inputs now accepts self.Rfree as a list of per-period arrays, but other methods (e.g., get_Rfree) still assume self.Rfree is a single NumPy array indexed directly by the Markov state. As a result, passing a list for Rfree will pass validation here but later fail or behave incorrectly when used, so either the rest of the class needs to be generalized to handle list-valued Rfree or this method should continue to enforce an array-only contract.

Suggested change
if isinstance(self.Rfree,list):
for rfree_t in self.Rfree:
# Check that arrays are the right shape
if not isinstance(rfree_t, np.ndarray) or rfree_t.shape != (StateCount,):
raise ValueError(
"Rfree not the right shape, it should an array of Rfree of all the states."
)
else:
# Check that arrays are the right shape
if not isinstance(self.Rfree, np.ndarray) or self.Rfree.shape != (StateCount,):
raise ValueError(
"Rfree not the right shape, it should an array of Rfree of all the states."
)
if not isinstance(self.Rfree, np.ndarray) or self.Rfree.shape != (StateCount,):
raise ValueError(
"Rfree not the right shape, it should be a NumPy array of Rfree for all the states."
)

Copilot uses AI. Check for mistakes.
Comment on lines +1385 to +1390
if type(self.Rfree)==list:
b_next_i = self.Rfree[k][0]*a_next_i
bNext.append(b_next_i)
else:
b_next_i = self.Rfree*a_next_i
bNext.append(b_next_i)
Copy link

Copilot AI Jan 28, 2026

Choose a reason for hiding this comment

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

In the finite-horizon branch of calc_transition_matrix, b_next_i is computed using either self.Rfree[k][0] * a_next_i or self.Rfree * a_next_i. This both ignores the dependence of the interest factor on the current Markov state (only the first entry is ever used from each period’s vector) and, when self.Rfree is a NumPy array of per-state rates, produces a 2D array that is inconsistent with the 1D bNext expected by gen_tran_matrix_1D/gen_tran_matrix_2D. The computation of b_next_i should use the scalar interest factor corresponding to state i in period k so that bNext[i] has shape matching a_next_i.

Suggested change
if type(self.Rfree)==list:
b_next_i = self.Rfree[k][0]*a_next_i
bNext.append(b_next_i)
else:
b_next_i = self.Rfree*a_next_i
bNext.append(b_next_i)
if type(self.Rfree) == list:
R_k = self.Rfree[k]
# Allow for either scalar or per-state array at period k
if np.ndim(R_k) == 0:
R_ki = R_k
else:
R_ki = R_k[i]
else:
# Allow for scalar or 1-D array of per-state rates
if np.ndim(self.Rfree) == 0:
R_ki = self.Rfree
else:
R_ki = self.Rfree[i]
b_next_i = R_ki * a_next_i
bNext.append(b_next_i)

Copilot uses AI. Check for mistakes.



if shk_dstn == None:
Copy link

Copilot AI Jan 28, 2026

Choose a reason for hiding this comment

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

Testing for None should use the 'is' operator.

Suggested change
if shk_dstn == None:
if shk_dstn is None:

Copilot uses AI. Check for mistakes.
elif self.T_cycle!= 0:

# for finite horizon, we can account for changing levels of prb_unemp because of endogenous job finding probability by imposing a list of these values, so for q'th period, the probability is slightly different
if shk_dstn == None:
Copy link

Copilot AI Jan 28, 2026

Choose a reason for hiding this comment

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

Testing for None should use the 'is' operator.

Suggested change
if shk_dstn == None:
if shk_dstn is None:

Copilot uses AI. Check for mistakes.
Comment on lines +923 to +925
c_first_col_0 = np.zeros(T)
a_first_col_0 = np.zeros(T)

Copy link

Copilot AI Jan 28, 2026

Choose a reason for hiding this comment

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

This assignment to 'c_first_col_0' is unnecessary as it is redefined before this value is used.

Suggested change
c_first_col_0 = np.zeros(T)
a_first_col_0 = np.zeros(T)

Copilot uses AI. Check for mistakes.
Comment on lines +923 to +925
c_first_col_0 = np.zeros(T)
a_first_col_0 = np.zeros(T)

Copy link

Copilot AI Jan 28, 2026

Choose a reason for hiding this comment

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

This assignment to 'a_first_col_0' is unnecessary as it is redefined before this value is used.

Suggested change
c_first_col_0 = np.zeros(T)
a_first_col_0 = np.zeros(T)

Copilot uses AI. Check for mistakes.
Comment on lines +1493 to +1496
def compute_steady_state(self,IncShkDstn_ntrl_msr):



Copy link

Copilot AI Jan 28, 2026

Choose a reason for hiding this comment

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

This method requires 2 positional arguments, whereas overridden IndShockConsumerType.compute_steady_state requires 1.

Suggested change
def compute_steady_state(self,IncShkDstn_ntrl_msr):
def compute_steady_state(self, IncShkDstn_ntrl_msr=None):
# Preserve previous behavior when the argument is omitted by
# explicitly raising a TypeError, similar to Python's default message.
if IncShkDstn_ntrl_msr is None:
raise TypeError(
"compute_steady_state() missing required positional argument "
"'IncShkDstn_ntrl_msr'"
)

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Stale

Development

Successfully merging this pull request may close these issues.

3 participants