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 c_future_wealth(fut_period = 1, coh = 1, exo = True): c_list = [] rebate_fut_vals = np.linspace(0, 1, num=11) rebate_curr_vals = rebate_fut_vals[1:] for rebate_fut in rebate_fut_vals: settings.rebate_size = rebate_fut settings.init() if exo: IndShockExample = Model.IndShockConsumerType(**baseline_params) else : IndShockExample = Model.IndShockConsumerType(**init_natural_borrowing_constraint) IndShockExample.solve() IndShockExample.unpack_cFunc() IndShockExample.timeFwd() c_list = np.append(c_list,IndShockExample.cFunc[t_eval-fut_period](coh)) for rebate_cur in rebate_curr_vals: c_list = np.append(c_list,IndShockExample.cFunc[t_eval-fut_period](coh+rebate_cur)) c_func = LinearInterp(np.linspace(0, 2, num=21),np.array(c_list)) return(c_func)
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]))
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
#construct results w default specification for how to set up house prices (neg wealth effect) uw_house_params = deepcopy(baseline_params) uw_house_params['rebate_amt'], e, uw_house_params['BoroCnstArt'], uw_house_params['HsgPay'] = \ hsg_wealth(initial_debt = hamp_params['baseline_debt'], **hamp_params) pra_params = deepcopy(baseline_params) pra_params['rebate_amt'], e, pra_params['BoroCnstArt'], pra_params['HsgPay'] = \ hsg_wealth(initial_debt = hamp_params['baseline_debt'] - hamp_params['pra_forgive'], **hamp_params) pra_params['HsgPay'] = pra_pmt(age=45, **hamp_params) #slide 3 -- housing equity labels = ["Payment Reduction", "Payment & Principal Reduction"] def neg(x): return -1 * x boro_cnst_pre_pra = LinearInterp( np.arange(26, 91), list(map(neg, uw_house_params['BoroCnstArt']))) boro_cnst_post_pra = LinearInterp( np.arange(26, 91), list(map(neg, pra_params['BoroCnstArt']))) mpl_funcs([boro_cnst_pre_pra, boro_cnst_post_pra], 45.001, 64, N=round(64 - 45.001), labels=labels, title="", ser_colors=[ "#9ecae1", "#3182bd", ], loc=(0, 0.7), ylab="Borrowing Limit (Years of Income)",
def solveFashion(solution_next,DiscFac,conformUtilityFunc,punk_utility,jock_utility,switchcost_J2P,switchcost_P2J,pGrid,pEvolution,pref_shock_mag): ''' Solves a single period of the fashion victim model. Parameters ---------- solution_next: FashionSolution A representation of the solution to the subsequent period's problem. DiscFac: float The intertemporal discount factor. conformUtilityFunc: function Utility as a function of the proportion of the population who wears the same style as the agent. punk_utility: float Direct utility from wearing the punk style this period. jock_utility: float Direct utility from wearing the jock style this period. switchcost_J2P: float Utility cost of switching from jock to punk this period. switchcost_P2J: float Utility cost of switching from punk to jock this period. pGrid: np.array 1D array of "proportion of punks" states spanning [0,1], representing the fraction of agents *currently* wearing punk style. pEvolution: np.array 2D array representing the distribution of next period's "proportion of punks". The pEvolution[i,:] contains equiprobable values of p for next period if p = pGrid[i] today. pref_shock_mag: float Standard deviation of T1EV preference shocks over style. Returns ------- solution_now: FashionSolution A representation of the solution to this period's problem. ''' # Unpack next period's solution VfuncPunkNext = solution_next.VfuncPunk VfuncJockNext = solution_next.VfuncJock # Calculate end-of-period expected value for each style at points on the pGrid EndOfPrdVpunk = DiscFac*np.mean(VfuncPunkNext(pEvolution),axis=1) EndOfPrdVjock = DiscFac*np.mean(VfuncJockNext(pEvolution),axis=1) # Get current period utility flow from each style (without switching cost) Upunk = punk_utility + conformUtilityFunc(pGrid) Ujock = jock_utility + conformUtilityFunc(1.0 - pGrid) # Calculate choice-conditional value for each combination of current and next styles (at each) V_J2J = Ujock + EndOfPrdVjock V_J2P = Upunk - switchcost_J2P + EndOfPrdVpunk V_P2J = Ujock - switchcost_P2J + EndOfPrdVjock V_P2P = Upunk + EndOfPrdVpunk # Calculate the beginning-of-period expected value of each p-state when punk Vboth_P = np.vstack((V_P2J,V_P2P)) Vbest_P = np.max(Vboth_P,axis=0) Vnorm_P = Vboth_P - np.tile(np.reshape(Vbest_P,(1,pGrid.size)),(2,1)) ExpVnorm_P = np.exp(Vnorm_P/pref_shock_mag) SumExpVnorm_P = np.sum(ExpVnorm_P,axis=0) V_P = np.log(SumExpVnorm_P)*pref_shock_mag + Vbest_P switch_P = ExpVnorm_P[0,:]/SumExpVnorm_P # Calculate the beginning-of-period expected value of each p-state when jock Vboth_J = np.vstack((V_J2J,V_J2P)) Vbest_J = np.max(Vboth_J,axis=0) Vnorm_J = Vboth_J - np.tile(np.reshape(Vbest_J,(1,pGrid.size)),(2,1)) ExpVnorm_J = np.exp(Vnorm_J/pref_shock_mag) SumExpVnorm_J = np.sum(ExpVnorm_J,axis=0) V_J = np.log(SumExpVnorm_J)*pref_shock_mag + Vbest_J switch_J = ExpVnorm_J[1,:]/SumExpVnorm_J # Make value and policy functions for each style VfuncPunkNow = LinearInterp(pGrid,V_P) VfuncJockNow = LinearInterp(pGrid,V_J) switchFuncPunkNow = LinearInterp(pGrid,switch_P) switchFuncJockNow = LinearInterp(pGrid,switch_J) # Make and return this period's solution solution_now = FashionSolution(VfuncJock=VfuncJockNow, VfuncPunk=VfuncPunkNow, switchFuncJock=switchFuncJockNow, switchFuncPunk=switchFuncPunkNow) return solution_now
class FashionVictimType(AgentType): ''' A class for representing types of agents in the fashion victim model. Agents make a binary decision over which style to wear (jock or punk), subject to switching costs. They receive utility directly from their chosen style and from the proportion of the population that wears the same style. ''' _solution_terminal = FashionSolution(VfuncJock=LinearInterp(np.array([0.0, 1.0]),np.array([0.0,0.0])), VfuncPunk=LinearInterp(np.array([0.0, 1.0]),np.array([0.0,0.0])), switchFuncJock=NullFunc(), switchFuncPunk=NullFunc()) def __init__(self,**kwds): ''' Instantiate a new FashionVictim with given data. Parameters ---------- **kwds : keyword arguments Any number of keyword arguments key=value; each value will be assigned to the attribute key in the new instance. Returns ------- new instance of FashionVictimType ''' # Initialize a basic AgentType AgentType.__init__(self,solution_terminal=FashionVictimType._solution_terminal,cycles=0,time_flow=True,pseudo_terminal=True,**kwds) # Add class-specific features self.time_inv = ['DiscFac','conformUtilityFunc','punk_utility','jock_utility','switchcost_J2P','switchcost_P2J','pGrid','pEvolution','pref_shock_mag'] self.time_vary = [] self.solveOnePeriod = solveFashion self.update() def updateEvolution(self): ''' Updates the "population punk proportion" evolution array. Fasion victims believe that the proportion of punks in the subsequent period is a linear function of the proportion of punks this period, subject to a uniform shock. Given attributes of self pNextIntercept, pNextSlope, pNextCount, pNextWidth, and pGrid, this method generates a new array for the attri- bute pEvolution, representing a discrete approximation of next period states for each current period state in pGrid. Parameters ---------- none Returns ------- none ''' self.pEvolution = np.zeros((self.pCount,self.pNextCount)) for j in range(self.pCount): pNow = self.pGrid[j] pNextMean = self.pNextIntercept + self.pNextSlope*pNow dist = approxUniform(N=self.pNextCount,bot=pNextMean-self.pNextWidth,top=pNextMean+self.pNextWidth)[1] self.pEvolution[j,:] = dist def update(self): ''' Updates the non-primitive objects needed for solution, using primitive attributes. This includes defining a "utility from conformity" function conformUtilityFunc, a grid of population punk proportions, and an array of future punk proportions (for each value in the grid). Results are stored as attributes of self. Parameters ---------- none Returns ------- none ''' self.conformUtilityFunc = lambda x : stats.beta.pdf(x,self.uParamA,self.uParamB) self.pGrid = np.linspace(0.0001,0.9999,self.pCount) self.updateEvolution() def reset(self): ''' Resets this agent type to prepare it for a new simulation run. This includes resetting the random number generator and initializing the style of each agent of this type. ''' self.resetRNG() sNow = np.zeros(self.pop_size) Shk = self.RNG.rand(self.pop_size) sNow[Shk < self.p_init] = 1 self.sNow = sNow def preSolve(self): ''' Updates the punk proportion evolution array by calling self.updateEvolution(). Parameters ---------- none Returns ------- none ''' # This step is necessary in the general equilibrium framework, where a # new evolution rule is calculated after each simulation run, but only # the sufficient statistics describing it are sent back to agents. self.updateEvolution() def postSolve(self): ''' Unpack the behavioral and value functions for more parsimonious access. Parameters ---------- none Returns ------- none ''' self.switchFuncPunk = self.solution[0].switchFuncPunk self.switchFuncJock = self.solution[0].switchFuncJock self.VfuncPunk = self.solution[0].VfuncPunk self.VfuncJock = self.solution[0].VfuncJock def simOnePrd(self): ''' Simulate one period of the fashion victom model for this type. Each agent receives an idiosyncratic preference shock and chooses whether to change styles (using the optimal decision rule). Parameters ---------- none Returns ------- none ''' pNow = self.pNow sPrev = self.sNow J2Pprob = self.switchFuncJock(pNow) P2Jprob = self.switchFuncPunk(pNow) Shks = self.RNG.rand(self.pop_size) J2P = np.logical_and(sPrev == 0,Shks < J2Pprob) P2J = np.logical_and(sPrev == 1,Shks < P2Jprob) sNow = copy(sPrev) sNow[J2P] = 1 sNow[P2J] = 0 self.sNow = sNow def marketAction(self): ''' The "market action" for a FashionType in the general equilibrium setting. Simulates a single period using self.simOnePrd(). Parameters ---------- none Returns ------- none ''' self.simOnePrd()
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 consumptionSavingSolverLinear(solution_tp1, income_distrib, p_zero_income, survival_prob, beta, rho, R, Gamma, constrained, a_grid): ''' Solves a single period of a standard consumption-saving problem, representing the consumption function as piecewise linear interpolation. Parameters: ----------- solution_tp1: ConsumerSolution The solution to the following period, likely generated by another call to SolveAPeriodConsumptionSaving. income_distrib: [[float]] A list containing three lists of floats, representing a discrete approximation to the income process between the period being solved and the one immediately following (in Solution_tp1). Order: psi, xi, probs p_zero_income: float The probability of receiving zero income in the succeeding period. survival_prob: float Probability of surviving to succeeding period. beta: float Discount factor between this period and the succeeding period. rho: float The coefficient of relative risk aversion R: float Interest factor on assets between this period and the succeeding period: w_tp1 = a_t*R Gamma: float Expected growth factor for permanent income between this period and the succeeding period. constrained: bool Whether this individual may hold net negative assets at the end of the period. a_grid: [float] A list of end-of-period asset values (post-decision states) at which to solve for optimal consumption. Returns: ----------- Solution_t: ConsumerSolution The solution to this period's problem, obtained using the method of endogenous gridpoints. ''' # Define utility functions u = lambda c: utility(c, gam=rho ) # currently unused, potentially useful for vFunc uP = lambda c: utilityP(c, gam=rho) uPinv = lambda u: utilityP_inv(u, gam=rho) # Set and update values for this period effective_beta = beta * survival_prob psi_tp1 = income_distrib[0] xi_tp1 = income_distrib[1] prob_tp1 = income_distrib[2] cFunc_tp1 = solution_tp1.cFunc psi_underbar_tp1 = np.min(psi_tp1) xi_underbar_tp1 = np.min(xi_tp1) # Calculate the minimum allowable value of money resources in this period if constrained: m_underbar_t = 0.0 else: m_underbar_t = (solution_tp1.m_underbar - xi_underbar_tp1) * (Gamma * psi_underbar_tp1) / R # Define the borrowing constraint (limiting consumption function) constraint_t = lambda m: m - m_underbar_t # Find the unconstrained consumption function one step back c_temp = [0.0 ] # Limiting consumption is zero as m approaches m lower bound m_temp = [m_underbar_t] a = np.asarray(a_grid) + m_underbar_t a_N = a.size shock_N = xi_tp1.size a_temp = np.tile(a, (shock_N, 1)) psi_temp = (np.tile(psi_tp1, (a_N, 1))).transpose() xi_temp = (np.tile(xi_tp1, (a_N, 1))).transpose() prob_temp = (np.tile(prob_tp1, (a_N, 1))).transpose() m_tp1 = R / (Gamma * psi_temp) * a_temp + xi_temp c_tp1 = cFunc_tp1(m_tp1) C_tp1 = psi_temp * c_tp1 gothicvP = effective_beta * R * Gamma**(-rho) * np.sum( uP(C_tp1) * prob_temp, axis=0) c = uPinv(gothicvP) m = c + a c_temp += c.tolist() m_temp += m.tolist() cFunc_t_unconstrained = LinearInterp(m_temp, c_temp) # Combine the constrained and unconstrained functions into the true consumption function cFunc_t = ConstrainedComposite(cFunc_t_unconstrained, constraint_t) # Store the results in a solution object and return it solution_t = ConsumerSolution(cFunc=cFunc_t, m_underbar=m_underbar_t) return solution_t
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
for ltv in ltv_rows: mtg_params = hsg_params(params_u, ltv=ltv, pra=True) agent_nd = solve_unpack(mtg_params) m_star, share_default = default_rate_solved(v_def_stig_tmp, agent_nd) m_u_pra_list.append(m_star) if ltv <= 100: share_default = 0 def_u_pra_list.append(share_default) mtg_params = hsg_params(params_std, ltv=ltv, pra=True) agent_nd = solve_unpack(mtg_params) m_star, share_default = default_rate_solved(v_def_stig_std, agent_nd) m_std_pra_list.append(m_star) if ltv <= 100: share_default = 0 def_std_pra_list.append(share_default) mstar_u_pra_f = LinearInterp(ltv_rows, np.array(m_u_pra_list)) def_u_pra_f = LinearInterp(ltv_rows, np.array(def_u_pra_list)) mstar_std_pra_f = LinearInterp(ltv_rows, np.array(m_std_pra_list)) def_std_pra_f = LinearInterp(ltv_rows, np.array(def_std_pra_list)) labels = ["Model: Baseline", "Model: Match Xsec Correlation"] labels_main = ["Baseline"] g = mpl_funcs([def_u_pra_f], min(ltv_rows), max(ltv_rows) + 10, N=len(ltv_rows), ser_colors=["#9ecae1"], show_legend=False, title="",
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