def usePointsForInterpolation(self, cNrm, mNrm, interpolator): ''' Make a basic solution object with a consumption function and marginal value function (unconditional on the preference shock). Parameters ---------- cNrm : np.array Consumption points for interpolation. mNrm : np.array Corresponding market resource points for interpolation. interpolator : function A function that constructs and returns a consumption function. Returns ------- solution_now : ConsumerSolution The solution to this period's consumption-saving problem, with a consumption function, marginal value function, and minimum m. ''' # Make the preference-shock specific consumption functions PrefShkCount = self.PrefShkVals.size cFunc_list = [] for j in range(PrefShkCount): MPCmin_j = self.MPCminNow * self.PrefShkVals[j]**(1.0 / self.CRRA) cFunc_this_shock = LowerEnvelope( LinearInterp(mNrm[j, :], cNrm[j, :], intercept_limit=self.hNrmNow * MPCmin_j, slope_limit=MPCmin_j), self.cFuncNowCnst) cFunc_list.append(cFunc_this_shock) # Combine the list of consumption functions into a single interpolation cFuncNow = LinearInterpOnInterp1D(cFunc_list, self.PrefShkVals) # Make the ex ante marginal value function (before the preference shock) m_grid = self.aXtraGrid + self.mNrmMinNow vP_vec = np.zeros_like(m_grid) for j in range( PrefShkCount): # numeric integration over the preference shock vP_vec += self.uP(cFunc_list[j]( m_grid)) * self.PrefShkPrbs[j] * self.PrefShkVals[j] vPnvrs_vec = self.uPinv(vP_vec) vPfuncNow = MargValueFunc(LinearInterp(m_grid, vPnvrs_vec), self.CRRA) # Store the results in a solution object and return it solution_now = ConsumerSolution(cFunc=cFuncNow, vPfunc=vPfuncNow, mNrmMin=self.mNrmMinNow) return solution_now
def updateSolutionTerminal(self): ''' Updates the terminal period solution for an aggregate shock consumer. Only fills in the consumption function and marginal value function. Parameters ---------- None Returns ------- None ''' vPfunc_terminal = lambda m,k : m**(-self.CRRA) cFunc_terminal = lambda m,k : m self.solution_terminal = ConsumerSolution(cFunc=cFunc_terminal,vPfunc=vPfunc_terminal)
def updateSolutionTerminal(self): ''' Updates the terminal period solution for an aggregate shock consumer. Only fills in the consumption function and marginal value function. Parameters ---------- None Returns ------- None ''' cFunc_terminal = BilinearInterp(np.array([[0.0, 0.0], [1.0, 1.0]]), np.array([0.0, 1.0]), np.array([0.0, 1.0])) vPfunc_terminal = MargValueFunc2D(cFunc_terminal, self.CRRA) mNrmMin_terminal = ConstantFunction(0) self.solution_terminal = ConsumerSolution(cFunc=cFunc_terminal, vPfunc=vPfunc_terminal, mNrmMin=mNrmMin_terminal)
def solveConsAggShock(solution_next, IncomeDstn, LivPrb, DiscFac, CRRA, PermGroFac, aXtraGrid, kGrid, kNextFunc, Rfunc, wFunc): ''' Solve one period of a consumption-saving problem with idiosyncratic and aggregate shocks (transitory and permanent). This is a basic solver that can't handle borrowing (assumes liquidity constraint) or cubic splines, nor can it calculate a value function. Parameters ---------- solution_next : ConsumerSolution The solution to the succeeding one period problem. IncomeDstn : [np.array] A list containing five arrays of floats, representing a discrete approximation to the income process between the period being solved and the one immediately following (in solution_next). Order: event probabilities, idisyncratic permanent shocks, idiosyncratic transitory shocks, aggregate permanent shocks, aggregate transitory shocks. 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. PermGroFac : float Expected permanent income growth factor at the end of this period. aXtraGrid : np.array Array of "extra" end-of-period asset values-- assets above the absolute minimum acceptable level. kGrid : np.array A grid of capital-to-labor ratios in the economy. kNextFunc : function Next period's capital-to-labor ratio as a function of this period's ratio. Rfunc : function The net interest factor on assets as a function of capital ratio k. wFunc : function The wage rate for labor as a function of capital-to-labor ratio k. Returns ------- solution_now : ConsumerSolution The solution to the single period consumption-saving problem. Includes a consumption function cFunc (linear interpolation over linear interpola- tions) and marginal value function vPfunc. ''' # Unpack next period's solution vPfuncNext = solution_next.vPfunc # Unpack the income shocks ShkPrbsNext = IncomeDstn[0] PermShkValsNext = IncomeDstn[1] TranShkValsNext = IncomeDstn[2] PermShkAggValsNext = IncomeDstn[3] TranShkAggValsNext = IncomeDstn[4] ShkCount = ShkPrbsNext.size # Make the grid of end-of-period asset values, and a tiled version aNrmNow = np.insert(aXtraGrid, 0, 0.0) aNrmNow_tiled = np.tile(aNrmNow, (ShkCount, 1)) aCount = aNrmNow.size # Make tiled versions of the income shocks ShkPrbsNext_tiled = (np.tile(ShkPrbsNext, (aCount, 1))).transpose() PermShkValsNext_tiled = (np.tile(PermShkValsNext, (aCount, 1))).transpose() TranShkValsNext_tiled = (np.tile(TranShkValsNext, (aCount, 1))).transpose() PermShkAggValsNext_tiled = (np.tile(PermShkAggValsNext, (aCount, 1))).transpose() TranShkAggValsNext_tiled = (np.tile(TranShkAggValsNext, (aCount, 1))).transpose() # Loop through the values in kGrid and calculate a linear consumption function for each cFuncByK_list = [] for j in range(kGrid.size): kNow = kGrid[j] kNext = kNextFunc(kNow) # Calculate returns to capital and labor in the next period kNextEff_array = kNext / TranShkAggValsNext_tiled Reff_array = Rfunc(kNextEff_array) / LivPrb # Effective interest rate wEff_array = wFunc( kNextEff_array ) * TranShkAggValsNext_tiled # Effective wage rate (accounts for labor supply) # Calculate market resources next period (and a constant array of capital-to-labor ratio) PermShkTotal_array = PermGroFac * PermShkValsNext_tiled * PermShkAggValsNext_tiled # total / combined permanent shock mNrmNext_array = Reff_array * aNrmNow_tiled / PermShkTotal_array + TranShkValsNext_tiled * wEff_array kNext_array = kNext * np.ones_like(mNrmNext_array) # Find marginal value next period at every income shock realization and every asset gridpoint vPnext_array = Reff_array * PermShkTotal_array**(-CRRA) * vPfuncNext( mNrmNext_array, kNext_array) # Calculate expectated marginal value at the end of the period at every asset gridpoint EndOfPrdvP = DiscFac * LivPrb * PermGroFac**(-CRRA) * np.sum( vPnext_array * ShkPrbsNext_tiled, axis=0) # Calculate optimal consumption from each asset gridpoint, and construct a linear interpolation cNrmNow = EndOfPrdvP**(-1.0 / CRRA) mNrmNow = aNrmNow + cNrmNow c_for_interpolation = np.insert( cNrmNow, 0, 0.0) # Add liquidity constrained portion m_for_interpolation = np.insert(mNrmNow, 0, 0.0) cFuncNow_j = LinearInterp(m_for_interpolation, c_for_interpolation) # Add the k-specific consumption function to the list cFuncByK_list.append(cFuncNow_j) # Construct the overall consumption function by combining the k-specific functions cFuncNow = LinearInterpOnInterp1D(cFuncByK_list, kGrid) # Construct the marginal value function using the envelope condition vPfuncNow = MargValueFunc2D(cFuncNow, CRRA) # Pack up and return the solution solution_now = ConsumerSolution(cFunc=cFuncNow, vPfunc=vPfuncNow) return solution_now
def solveConsAggShock(solution_next, IncomeDstn, LivPrb, DiscFac, CRRA, PermGroFac, aXtraGrid, BoroCnstArt, Mgrid, AFunc, Rfunc, wFunc, DeprFac): ''' Solve one period of a consumption-saving problem with idiosyncratic and aggregate shocks (transitory and permanent). This is a basic solver that can't handle borrowing (assumes liquidity constraint) or cubic splines, nor can it calculate a value function. Parameters ---------- solution_next : ConsumerSolution The solution to the succeeding one period problem. IncomeDstn : [np.array] A list containing five arrays of floats, representing a discrete approximation to the income process between the period being solved and the one immediately following (in solution_next). Order: event probabilities, idisyncratic permanent shocks, idiosyncratic transitory shocks, aggregate permanent shocks, aggregate transitory shocks. 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. PermGroGac : float Expected permanent income growth factor at the end of this period. aXtraGrid : np.array Array of "extra" end-of-period asset values-- assets above the absolute minimum acceptable level. BoroCnstArt : float Artificial borrowing constraint; minimum allowable end-of-period asset-to- permanent-income ratio. Unlike other models, this *can't* be None. Mgrid : np.array A grid of aggregate market resourses to permanent income in the economy. AFunc : function Aggregate savings as a function of aggregate market resources. Rfunc : function The net interest factor on assets as a function of capital ratio k. wFunc : function The wage rate for labor as a function of capital-to-labor ratio k. DeprFac : float Capital Depreciation Rate Returns ------- solution_now : ConsumerSolution The solution to the single period consumption-saving problem. Includes a consumption function cFunc (linear interpolation over linear interpola- tions) and marginal value function vPfunc. ''' # Unpack next period's solution vPfuncNext = solution_next.vPfunc mNrmMinNext = solution_next.mNrmMin # Unpack the income shocks ShkPrbsNext = IncomeDstn[0] PermShkValsNext = IncomeDstn[1] TranShkValsNext = IncomeDstn[2] PermShkAggValsNext = IncomeDstn[3] TranShkAggValsNext = IncomeDstn[4] ShkCount = ShkPrbsNext.size # Make the grid of end-of-period asset values, and a tiled version aNrmNow = np.insert(aXtraGrid, 0, 0.0) aNrmNow_tiled = np.tile(aNrmNow, (ShkCount, 1)) aCount = aNrmNow.size # Make tiled versions of the income shocks ShkPrbsNext_tiled = (np.tile(ShkPrbsNext, (aCount, 1))).transpose() PermShkValsNext_tiled = (np.tile(PermShkValsNext, (aCount, 1))).transpose() TranShkValsNext_tiled = (np.tile(TranShkValsNext, (aCount, 1))).transpose() PermShkAggValsNext_tiled = (np.tile(PermShkAggValsNext, (aCount, 1))).transpose() TranShkAggValsNext_tiled = (np.tile(TranShkAggValsNext, (aCount, 1))).transpose() # Loop through the values in Mgrid and calculate a linear consumption function for each cFuncBaseByM_list = [] BoroCnstNat_array = np.zeros(Mgrid.size) mNrmMinNext_array = mNrmMinNext(AFunc(Mgrid) * (1 - DeprFac)) for j in range(Mgrid.size): MNow = Mgrid[j] AaggNow = AFunc(MNow) # Calculate returns to capital and labor in the next period kNext_array = AaggNow * (1 - DeprFac) / ( PermGroFac * PermShkAggValsNext_tiled * TranShkAggValsNext_tiled) kNextEff_array = kNext_array / TranShkAggValsNext_tiled R_array = Rfunc(kNextEff_array) # Interest factor on aggregate assets Reff_array = R_array / LivPrb # Effective interest factor on individual assets *for survivors* wEff_array = wFunc( kNextEff_array ) * TranShkAggValsNext_tiled # Effective wage rate (accounts for labor supply) PermShkTotal_array = PermGroFac * PermShkValsNext_tiled * PermShkAggValsNext_tiled # total / combined permanent shock Mnext_array = kNext_array * (R_array + DeprFac) + wEff_array # Find the natural borrowing constraint for this capital-to-labor ratio aNrmMin_candidates = PermGroFac * PermShkValsNext * PermShkAggValsNext / Reff_array[:, 0] * ( mNrmMinNext_array[j] - wEff_array[:, 0] * TranShkValsNext) aNrmMin = np.max(aNrmMin_candidates) BoroCnstNat = aNrmMin BoroCnstNat_array[j] = BoroCnstNat # Calculate market resources next period (and a constant array of capital-to-labor ratio) mNrmNext_array = Reff_array * ( aNrmNow_tiled + aNrmMin) / PermShkTotal_array + TranShkValsNext_tiled * wEff_array # Find marginal value next period at every income shock realization and every aggregate market resource gridpoint vPnext_array = Reff_array * PermShkTotal_array**(-CRRA) * vPfuncNext( mNrmNext_array, Mnext_array) # Calculate expectated marginal value at the end of the period at every asset gridpoint EndOfPrdvP = DiscFac * LivPrb * PermGroFac**(-CRRA) * np.sum( vPnext_array * ShkPrbsNext_tiled, axis=0) # Calculate optimal consumption from each asset gridpoint, and construct a linear interpolation cNrmNow = EndOfPrdvP**(-1.0 / CRRA) mNrmNow = (aNrmNow + aNrmMin) + cNrmNow c_for_interpolation = np.insert( cNrmNow, 0, 0.0) # Add liquidity constrained portion m_for_interpolation = np.insert(mNrmNow - BoroCnstNat, 0, 0.0) cFuncBase_j = LinearInterp(m_for_interpolation, c_for_interpolation) # Add the M-specific consumption function to the list cFuncBaseByM_list.append(cFuncBase_j) # Construct the overall unconstrained consumption function by combining the M-specific functions BoroCnstNat = LinearInterp(np.insert(Mgrid, 0, 0.0), np.insert(BoroCnstNat_array, 0, 0.0)) cFuncBase = LinearInterpOnInterp1D(cFuncBaseByM_list, Mgrid) cFuncUnc = VariableLowerBoundFunc2D(cFuncBase, BoroCnstNat) # Make the constrained consumption function and combine it with the unconstrained component cFuncCnst = BilinearInterp(np.array([[0.0, 0.0], [1.0, 1.0]]), np.array([BoroCnstArt, BoroCnstArt + 1.0]), np.array([0.0, 1.0])) cFuncNow = LowerEnvelope2D(cFuncUnc, cFuncCnst) # Make the minimum m function as the greater of the natural and artificial constraints mNrmMinNow = UpperEnvelope(BoroCnstNat, ConstantFunction(BoroCnstArt)) # Construct the marginal value function using the envelope condition vPfuncNow = MargValueFunc2D(cFuncNow, CRRA) # Pack up and return the solution solution_now = ConsumerSolution(cFunc=cFuncNow, vPfunc=vPfuncNow, mNrmMin=mNrmMinNow) return solution_now
def makeSolution(self,cNrm,mNrm): ''' Construct an object representing the solution to this period's problem. Parameters ---------- cNrm : np.array Array of normalized consumption values for interpolation. Each row corresponds to a Markov state for this period. mNrm : np.array Array of normalized market resource values for interpolation. Each row corresponds to a Markov state for this period. Returns ------- solution : ConsumerSolution The solution to the single period consumption-saving problem. Includes a consumption function cFunc (using cubic or linear splines), a marg- inal value function vPfunc, a minimum acceptable level of normalized market resources mNrmMin, normalized human wealth hNrm, and bounding MPCs MPCmin and MPCmax. It might also have a value function vFunc and marginal marginal value function vPPfunc. All of these attributes are lists or arrays, with elements corresponding to the current Markov state. E.g. solution.cFunc[0] is the consumption function when in the i=0 Markov state this period. ''' solution = ConsumerSolution() # An empty solution to which we'll add state-conditional solutions # Calculate the MPC at each market resource gridpoint in each state (if desired) if self.CubicBool: dcda = self.EndOfPrdvPP/self.uPP(np.array(self.cNrmNow)) MPC = dcda/(dcda+1.0) self.MPC_temp = np.hstack((np.reshape(self.MPCmaxNow,(self.StateCount,1)),MPC)) interpfunc = self.makeCubiccFunc else: interpfunc = self.makeLinearcFunc # Loop through each current period state and add its solution to the overall solution for i in range(self.StateCount): # Set current-period-conditional human wealth and MPC bounds self.hNrmNow_j = self.hNrmNow[i] self.MPCminNow_j = self.MPCminNow[i] if self.CubicBool: self.MPC_temp_j = self.MPC_temp[i,:] # Construct the consumption function by combining the constrained and unconstrained portions self.cFuncNowCnst = LinearInterp([self.mNrmMin_list[i], self.mNrmMin_list[i]+1.0], [0.0,1.0]) cFuncNowUnc = interpfunc(mNrm[i,:],cNrm[i,:]) cFuncNow = LowerEnvelope(cFuncNowUnc,self.cFuncNowCnst) # Make the marginal value function and pack up the current-state-conditional solution vPfuncNow = MargValueFunc(cFuncNow,self.CRRA) solution_cond = ConsumerSolution(cFunc=cFuncNow, vPfunc=vPfuncNow, mNrmMin=self.mNrmMinNow) if self.CubicBool: # Add the state-conditional marginal marginal value function (if desired) solution_cond = self.addvPPfunc(solution_cond) # Add the current-state-conditional solution to the overall period solution solution.appendSolution(solution_cond) # Add the lower bounds of market resources, MPC limits, human resources, # and the value functions to the overall solution solution.mNrmMin = self.mNrmMin_list solution = self.addMPCandHumanWealth(solution) if self.vFuncBool: vFuncNow = self.makevFunc(solution) solution.vFunc = vFuncNow # Return the overall solution to this period return solution
def solveConsRepAgent(solution_next, DiscFac, CRRA, IncomeDstn, CapShare, DeprFac, PermGroFac, aXtraGrid): ''' Solve one period of the simple representative agent consumption-saving model. Parameters ---------- solution_next : ConsumerSolution Solution to the next period's problem (i.e. previous iteration). DiscFac : float Intertemporal discount factor for future utility. CRRA : float Coefficient of relative risk aversion. IncomeDstn : [np.array] A list containing three arrays of floats, representing a discrete approximation to the income process between the period being solved and the one immediately following (in solution_next). Order: event probabilities, permanent shocks, transitory shocks. CapShare : float Capital's share of income in Cobb-Douglas production function. DeprFac : float Depreciation rate of capital. PermGroFac : float Expected permanent income growth factor at the end of this period. aXtraGrid : np.array Array of "extra" end-of-period asset values-- assets above the absolute minimum acceptable level. In this model, the minimum acceptable level is always zero. Returns ------- solution_now : ConsumerSolution Solution to this period's problem (new iteration). ''' # Unpack next period's solution and the income distribution vPfuncNext = solution_next.vPfunc ShkPrbsNext = IncomeDstn[0] PermShkValsNext = IncomeDstn[1] TranShKValsNext = IncomeDstn[2] # Make tiled versions of end-of-period assets, shocks, and probabilities aNrmNow = aXtraGrid aNrmCount = aNrmNow.size ShkCount = ShkPrbsNext.size aNrm_tiled = np.tile(np.reshape(aNrmNow, (aNrmCount, 1)), (1, ShkCount)) # Tile arrays of the income shocks and put them into useful shapes PermShkVals_tiled = np.tile(np.reshape(PermShkValsNext, (1, ShkCount)), (aNrmCount, 1)) TranShkVals_tiled = np.tile(np.reshape(TranShKValsNext, (1, ShkCount)), (aNrmCount, 1)) ShkPrbs_tiled = np.tile(np.reshape(ShkPrbsNext, (1, ShkCount)), (aNrmCount, 1)) # Calculate next period's capital-to-permanent-labor ratio under each combination # of end-of-period assets and shock realization kNrmNext = aNrm_tiled / (PermGroFac * PermShkVals_tiled) # Calculate next period's market resources KtoLnext = kNrmNext / TranShkVals_tiled RfreeNext = 1. - DeprFac + CapShare * KtoLnext**(CapShare - 1.) wRteNext = (1. - CapShare) * KtoLnext**CapShare mNrmNext = RfreeNext * kNrmNext + wRteNext * TranShkVals_tiled # Calculate end-of-period marginal value of assets for the RA vPnext = vPfuncNext(mNrmNext) EndOfPrdvP = DiscFac * np.sum( RfreeNext * (PermGroFac * PermShkVals_tiled)**(-CRRA) * vPnext * ShkPrbs_tiled, axis=1) # Invert the first order condition to get consumption, then find endogenous gridpoints cNrmNow = EndOfPrdvP**(-1. / CRRA) mNrmNow = aNrmNow + cNrmNow # Construct the consumption function and the marginal value function cFuncNow = LinearInterp(np.insert(mNrmNow, 0, 0.0), np.insert(cNrmNow, 0, 0.0)) vPfuncNow = MargValueFunc(cFuncNow, CRRA) # Construct and return the solution for this period solution_now = ConsumerSolution(cFunc=cFuncNow, vPfunc=vPfuncNow) return solution_now
def solveConsRepAgentMarkov(solution_next, MrkvArray, DiscFac, CRRA, IncomeDstn, CapShare, DeprFac, PermGroFac, aXtraGrid): ''' Solve one period of the simple representative agent consumption-saving model. This version supports a discrete Markov process. Parameters ---------- solution_next : ConsumerSolution Solution to the next period's problem (i.e. previous iteration). MrkvArray : np.array Markov transition array between this period and next period. DiscFac : float Intertemporal discount factor for future utility. CRRA : float Coefficient of relative risk aversion. IncomeDstn : [[np.array]] A list of lists containing three arrays of floats, representing a discrete approximation to the income process between the period being solved and the one immediately following (in solution_next). Order: event probabilities, permanent shocks, transitory shocks. CapShare : float Capital's share of income in Cobb-Douglas production function. DeprFac : float Depreciation rate of capital. PermGroFac : [float] Expected permanent income growth factor for each state we could be in next period. aXtraGrid : np.array Array of "extra" end-of-period asset values-- assets above the absolute minimum acceptable level. In this model, the minimum acceptable level is always zero. Returns ------- solution_now : ConsumerSolution Solution to this period's problem (new iteration). ''' # Define basic objects StateCount = MrkvArray.shape[0] aNrmNow = aXtraGrid aNrmCount = aNrmNow.size EndOfPrdvP_cond = np.zeros((StateCount, aNrmCount)) + np.nan # Loop over *next period* states, calculating conditional EndOfPrdvP for j in range(StateCount): # Define next-period-state conditional objects vPfuncNext = solution_next.vPfunc[j] ShkPrbsNext = IncomeDstn[j][0] PermShkValsNext = IncomeDstn[j][1] TranShKValsNext = IncomeDstn[j][2] # Make tiled versions of end-of-period assets, shocks, and probabilities ShkCount = ShkPrbsNext.size aNrm_tiled = np.tile(np.reshape(aNrmNow, (aNrmCount, 1)), (1, ShkCount)) # Tile arrays of the income shocks and put them into useful shapes PermShkVals_tiled = np.tile(np.reshape(PermShkValsNext, (1, ShkCount)), (aNrmCount, 1)) TranShkVals_tiled = np.tile(np.reshape(TranShKValsNext, (1, ShkCount)), (aNrmCount, 1)) ShkPrbs_tiled = np.tile(np.reshape(ShkPrbsNext, (1, ShkCount)), (aNrmCount, 1)) # Calculate next period's capital-to-permanent-labor ratio under each combination # of end-of-period assets and shock realization kNrmNext = aNrm_tiled / (PermGroFac[j] * PermShkVals_tiled) # Calculate next period's market resources KtoLnext = kNrmNext / TranShkVals_tiled RfreeNext = 1. - DeprFac + CapShare * KtoLnext**(CapShare - 1.) wRteNext = (1. - CapShare) * KtoLnext**CapShare mNrmNext = RfreeNext * kNrmNext + wRteNext * TranShkVals_tiled # Calculate end-of-period marginal value of assets for the RA vPnext = vPfuncNext(mNrmNext) EndOfPrdvP_cond[j, :] = DiscFac * np.sum( RfreeNext * (PermGroFac[j] * PermShkVals_tiled)**(-CRRA) * vPnext * ShkPrbs_tiled, axis=1) # Apply the Markov transition matrix to get unconditional end-of-period marginal value EndOfPrdvP = np.dot(MrkvArray, EndOfPrdvP_cond) # Construct the consumption function and marginal value function for each discrete state cFuncNow_list = [] vPfuncNow_list = [] for i in range(StateCount): # Invert the first order condition to get consumption, then find endogenous gridpoints cNrmNow = EndOfPrdvP[i, :]**(-1. / CRRA) mNrmNow = aNrmNow + cNrmNow # Construct the consumption function and the marginal value function cFuncNow_list.append( LinearInterp(np.insert(mNrmNow, 0, 0.0), np.insert(cNrmNow, 0, 0.0))) vPfuncNow_list.append(MargValueFunc(cFuncNow_list[-1], CRRA)) # Construct and return the solution for this period solution_now = ConsumerSolution(cFunc=cFuncNow_list, vPfunc=vPfuncNow_list) return solution_now
class PrefLaborConsumerType(IndShockConsumerType): ''' A consumer type with idiosyncratic shocks to permanent and transitory income, as well as shocks to preferences ''' # Define some universal values for all consumer types cFunc_terminal_ = BilinearInterpOnInterp1D( [[ LinearInterp(np.array([0.0, 1.0]), np.array([0.0, 1.0])), LinearInterp(np.array([0.0, 1.0]), np.array([0.0, 1.0])) ], [ LinearInterp(np.array([0.0, 1.0]), np.array([0.0, 1.0])), LinearInterp(np.array([0.0, 1.0]), np.array([0.0, 1.0])) ]], np.array([0.0, 1.0]), np.array([0.0, 1.0])) # c=m in terminal period vFunc_terminal_ = BilinearInterpOnInterp1D( [[ LinearInterp(np.array([0.0, 1.0]), np.array([0.0, 1.0])), LinearInterp(np.array([0.0, 1.0]), np.array([0.0, 1.0])) ], [ LinearInterp(np.array([0.0, 1.0]), np.array([0.0, 1.0])), LinearInterp(np.array([0.0, 1.0]), np.array([0.0, 1.0])) ]], np.array([0.0, 1.0]), np.array([0.0, 1.0])) # This is overwritten solution_terminal_ = ConsumerSolution(cFunc=cFunc_terminal_, vFunc=vFunc_terminal_) time_inv_ = PerfForesightConsumerType.time_inv_ + [ 'LaborElas', 'PrefShkVals', 'WageShkVals' ] shock_vars_ = ['PermShkNow', 'TranShkNow', 'PrefShkNow'] def __init__(self, cycles=1, time_flow=True, **kwds): ''' Instantiate a new ConsumerType with given data. See ConsumerParameters.init_idiosyncratic_shocks for a dictionary of the keywords that should be passed to the constructor. Parameters ---------- cycles : int Number of times the sequence of periods should be solved. time_flow : boolean Whether time is currently "flowing" forward for this instance. Returns ------- None ''' # Initialize a basic AgentType IndShockConsumerType.__init__(self, cycles=cycles, time_flow=time_flow, **kwds) # Add consumer-type specific objects, copying to create independent versions self.solveOnePeriod = solvePrefLaborShock # idiosyncratic shocks solver self.update() # Make assets grid, income process, terminal solution def reset(self): self.initializeSim() self.t_age = drawDiscrete(self.AgentCount, P=self.AgeDstn, X=np.arange(self.AgeDstn.size), exact_match=False, seed=self.RNG.randint(0, 2**31 - 1)).astype(int) self.t_cycle = copy(self.t_age) def marketAction(self): self.simulate(1) def updateIncomeProcess(self): ''' Updates this agent's income process based on his own attributes. The function that generates the discrete income process can be swapped out for a different process. Parameters ---------- none Returns: ----------- none ''' original_time = self.time_flow self.timeFwd() IncomeAndPrefDstn, PermShkDstn, TranShkDstn, PrefShkDstn = constructLognormalIncomeAndPreferenceProcess( self) self.IncomeAndPrefDstn = IncomeAndPrefDstn self.PermShkDstn = PermShkDstn self.TranShkDstn = TranShkDstn self.PrefShkDstn = TranShkDstn self.PrefShkVals = PrefShkDstn[0][1] self.WageShkVals = TranShkDstn[0][1] self.addToTimeVary('IncomeAndPrefDstn', 'PermShkDstn', 'TranShkDstn', 'PrefShkDstn') if not original_time: self.timeRev() def updateSolutionTerminal(self): ''' Update the terminal period solution. Parameters ---------- none Returns ------- none ''' #self.solution_terminal.vFunc = ValueFunc(self.cFunc_terminal_,self.CRRA) terminal_instance = LinearInterp(np.array([0.0, 100.0]), np.array([0.01, 0.0]), lower_extrap=True) self.solution_terminal.vPfunc = BilinearInterpOnInterp1D( [[terminal_instance, terminal_instance], [terminal_instance, terminal_instance]], np.array([0.0, 2.0]), np.array([0.0, 2.0])) #self.solution_terminal.vPPfunc = MargMargValueFunc(self.cFunc_terminal_,self.CRRA) def getShocks(self): ''' Gets permanent and transitory income shocks for this period. Samples from IncomeDstn for each period in the cycle. Parameters ---------- None Returns ------- None ''' PermShkNow = np.zeros(self.AgentCount) # Initialize shock arrays TranShkNow = np.zeros(self.AgentCount) PrefShkNow = np.zeros(self.AgentCount) newborn = self.t_age == 0 for t in range(self.T_cycle): these = t == self.t_cycle N = np.sum(these) if N > 0: IncomeDstnNow = self.IncomeAndPrefDstn[ t - 1] # set current income distribution PermGroFacNow = self.PermGroFac[ t - 1] # and permanent growth factor Indices = np.arange( IncomeDstnNow[0].size) # just a list of integers # Get random draws of income shocks from the discrete distribution EventDraws = drawDiscrete(N, X=Indices, P=IncomeDstnNow[0], exact_match=False, seed=self.RNG.randint(0, 2**31 - 1)) PermShkNow[these] = IncomeDstnNow[1][ EventDraws] * PermGroFacNow # permanent "shock" includes expected growth TranShkNow[these] = IncomeDstnNow[2][EventDraws] PrefShkNow[these] = IncomeDstnNow[3][EventDraws] # That procedure used the *last* period in the sequence for newborns, but that's not right # Redraw shocks for newborns, using the *first* period in the sequence. Approximation. N = np.sum(newborn) if N > 0: these = newborn IncomeDstnNow = self.IncomeAndPrefDstn[ 0] # set current income distribution PermGroFacNow = self.PermGroFac[0] # and permanent growth factor Indices = np.arange( IncomeDstnNow[0].size) # just a list of integers # Get random draws of income shocks from the discrete distribution EventDraws = drawDiscrete(N, X=Indices, P=IncomeDstnNow[0], exact_match=False, seed=self.RNG.randint(0, 2**31 - 1)) PermShkNow[these] = IncomeDstnNow[1][ EventDraws] * PermGroFacNow # permanent "shock" includes expected growth TranShkNow[these] = IncomeDstnNow[2][EventDraws] PrefShkNow[these] = IncomeDstnNow[3][EventDraws] # PermShkNow[newborn] = 1.0 TranShkNow[newborn] = 1.0 # Store the shocks in self self.EmpNow = np.ones(self.AgentCount, dtype=bool) self.EmpNow[TranShkNow == self.IncUnemp] = False self.PermShkNow = PermShkNow self.TranShkNow = TranShkNow self.PrefShkNow = PrefShkNow def getStates(self): ''' Calculates updated values of normalized market resources and permanent income level for each agent. Uses pLvlNow, aNrmNow, PermShkNow, TranShkNow. Parameters ---------- None Returns ------- None ''' pLvlPrev = self.pLvlNow aNrmPrev = self.aNrmNow RfreeNow = self.getRfree() # Calculate new states: normalized market resources and permanent income level self.pLvlNow = pLvlPrev * self.PermShkNow # Updated permanent income level ReffNow = RfreeNow / self.PermShkNow # "Effective" interest factor on normalized assets self.bNrmNow = ReffNow * aNrmPrev # Bank balances before labor income return None def getControls(self): ''' Calculates consumption for each consumer of this type using the consumption functions. Parameters ---------- None Returns ------- None ''' cNrmNow = np.zeros(self.AgentCount) + np.nan MPCnow = np.zeros(self.AgentCount) + np.nan lNow = np.zeros(self.AgentCount) + np.nan for t in range(self.T_cycle): these = t == self.t_cycle cNrmNow[these] = self.solution[t].cFunc(self.bNrmNow[these], self.TranShkNow, self.PrefShkNow) MPCnow[these] = self.solution[t].cFunc.derivativeX( self.bNrmNow[these], self.TranShkNow, self.PrefShkNow) lNow[these] = self.solution[t].lFunc(self.bNrmNow[these], self.TranShkNow, self.PrefShkNow) self.cNrmNow = cNrmNow self.MPCnow = MPCnow self.lNow = lNow self.lIncomeLvl = lNow * self.TranShkNow * self.pLvlNow self.cLvlNow = cNrmNow * self.pLvlNow return None def getPostStates(self): ''' Calculates end-of-period assets for each consumer of this type. Parameters ---------- None Returns ------- None ''' self.aNrmNow = self.bNrmNow + self.lNow * self.TranShkNow - self.cNrmNow self.aLvlNow = self.aNrmNow * self.pLvlNow # Useful in some cases to precalculate asset level return None
def makeBasicSolution(self, EndOfPrdvP, aNrm, wageShkVals, prefShkVals): ''' Given end of period assets and end of period marginal value, construct the basic solution for this period. Parameters ---------- EndOfPrdvP : np.array Array of end-of-period marginal values. aNrm : np.array Array of end-of-period asset values that yield the marginal values in EndOfPrdvP. wageShkVals : np.array Array of this period transitory wage shock values. prefShkVals : np.array Array of this period preference shock values. Returns ------- solution_now : ConsumerSolution The solution to this period's consumption-saving problem, with a consumption function, marginal value function. ''' num_pref_shocks = len(prefShkVals) num_wage_shocks = len(wageShkVals) cFuncBaseByPref_list = [] vPFuncBaseByPref_list = [] lFuncBaseByPref_list = [] for i in range(num_wage_shocks): cFuncBaseByPref_list.append([]) vPFuncBaseByPref_list.append([]) lFuncBaseByPref_list.append([]) for j in range(num_pref_shocks): c_temp = self.uPinv(EndOfPrdvP / prefShkVals[j]) l_temp = self.LabSupply(wageShkVals[i] * EndOfPrdvP) b_temp = c_temp + aNrm - l_temp * wageShkVals[i] if wageShkVals[i] == 0.0: c_temp = np.insert(c_temp, 0, 0., axis=-1) l_temp = np.insert(l_temp, 0, 0.0, axis=-1) b_temp = np.insert(b_temp, 0, 0.0, axis=-1) lFuncBaseByPref_list[i].append( LinearInterp(b_temp, l_temp, lower_extrap=True)) cFunc1 = LinearInterp(b_temp, c_temp, lower_extrap=True) cFunc2 = LinearInterp(b_temp, l_temp * wageShkVals[i] + b_temp, lower_extrap=True) cFuncBaseByPref_list[i].append(LowerEnvelope(cFunc1, cFunc2)) pseudo_inverse_vPfunc1 = LinearInterp( b_temp, prefShkVals[j]**(-1.0 / self.CRRA) * c_temp, lower_extrap=True) pseudo_inverse_vPfunc2 = LinearInterp( b_temp, prefShkVals[j]**(-1.0 / self.CRRA) * (l_temp * wageShkVals[i] + b_temp), lower_extrap=True) pseudo_inverse_vPfunc = LowerEnvelope(pseudo_inverse_vPfunc1, pseudo_inverse_vPfunc2) vPFuncBaseByPref_list[i].append( MargValueFunc(pseudo_inverse_vPfunc, self.CRRA)) cFuncNow = BilinearInterpOnInterp1D(cFuncBaseByPref_list, wageShkVals, prefShkVals) vPfuncNow = BilinearInterpOnInterp1D(vPFuncBaseByPref_list, wageShkVals, prefShkVals) lFuncNow = BilinearInterpOnInterp1D(lFuncBaseByPref_list, wageShkVals, prefShkVals) # Pack up and return the solution solution_now = ConsumerSolution(cFunc=cFuncNow, vPfunc=vPfuncNow) solution_now.lFunc = lFuncNow return solution_now