Beispiel #1
0
    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
Beispiel #2
0
 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)
Beispiel #3
0
 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)
Beispiel #4
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
Beispiel #5
0
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
Beispiel #6
0
    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
Beispiel #7
0
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
Beispiel #8
0
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