Example #1
0
 def cupyfy(self):
     # this sends copies of important objects to cupy
     # only things that are going to be reused are worth storing
     
     
     if not cupy:
         self.cupy = None
         return
     
     stuff = dict()
     
     stuff['theta_orig_on_fine'] = cp.array(self.theta_orig_on_fine,dtype=self.dtype)
     stuff['thetagrid'] = cp.array(self.thetagrid,dtype=self.dtype)
     stuff['thetagrid_fine'] = cp.array(self.thetagrid_fine,dtype=self.dtype)
     stuff['v_thetagrid_fine'] = VecOnGrid(stuff['thetagrid'],stuff['thetagrid_fine'],force_cupy=True) 
     stuff['agrid_c'] = cp.array(self.agrid_c,dtype=self.dtype)
     stuff['agrid_s'] = cp.array(self.agrid_s,dtype=self.dtype)
     stuff['sgrid_c'] = cp.array(self.sgrid_c,dtype=self.dtype)
     stuff['sgrid_s'] = cp.array(self.sgrid_s,dtype=self.dtype)
     stuff['vsgrid_c'] = VecOnGrid(self.agrid_c,self.sgrid_c,force_cupy=True)
     stuff['vsgrid_s'] = VecOnGrid(self.agrid_s,self.sgrid_s,force_cupy=True)
     stuff['mgrid_c'] = cp.array(self.mgrid_c,dtype=self.dtype)
     stuff['mgrid_s'] = cp.array(self.mgrid_s,dtype=self.dtype)
     
     uu = self.u_precomputed
     upc = {key : {e: cp.array(uu[key][e]) for e in uu[key]} for key in uu}
     stuff['u_precomputed'] = upc
     
     self.cupy = namedtuple('cupy',stuff.keys())(**stuff)
Example #2
0
    def get_asset_grids(self):
        self.agrids = dict()
        g = self.agrids
        factor_c = 2.0
        pwr = 0.0
        pivot = 0.1

        g['na_s'] = 40
        g['na_c'] = 50
        g['amin'] = 0.0
        g['amax_s'] = 40.0
        g['amax_c'] = factor_c * g['amax_s']

        def flex_space(amin, amax, na):
            if pwr == 0.0:
                return -pivot + np.exp(
                    np.linspace(np.log(amin + pivot), np.log(amax + pivot),
                                na))
            else:
                return -pivot + (np.linspace(
                    (amin + pivot)**pwr, (amax + pivot)**pwr, na))**(1 / pwr)

        g['agrid_s'] = flex_space(g['amin'], g['amax_s'], g['na_s'])
        g['agrid_c'] = flex_space(g['amin'], g['amax_c'], g['na_c'])

        n_between = 7
        da_min = 0.01
        da_max = 0.25

        g['sgrid_s'] = build_s_grid(g['agrid_s'], n_between, da_min, da_max)
        g['sgrid_c'] = build_s_grid(g['agrid_c'], n_between, da_min, da_max)

        g['v_sgrid_s'] = VecOnGrid(g['agrid_s'], g['sgrid_s'])
        g['v_sgrid_c'] = VecOnGrid(g['agrid_c'], g['sgrid_c'])
Example #3
0
def v_div_byshare(setup,
                  dc,
                  t,
                  sc,
                  share_fem,
                  share_mal,
                  Vmale,
                  Vfemale,
                  izf,
                  izm,
                  shrs=None,
                  cost_fem=0.0,
                  cost_mal=0.0):
    # this produces value of divorce for gridpoints given possibly different
    # shares of how assets are divided.
    # Returns Vf_divorce, Vm_divorce -- values of singles in case of divorce
    # matched to the gridpionts for couples

    # optional cost_fem and cost_mal are monetary costs of divorce
    if shrs is None: shrs = shrs_def

    Vm_divorce_M, Vf_divorce_M = v_div_allsplits(setup,
                                                 dc,
                                                 t,
                                                 sc,
                                                 Vmale,
                                                 Vfemale,
                                                 izm,
                                                 izf,
                                                 shrs=shrs,
                                                 cost_fem=cost_fem,
                                                 cost_mal=cost_mal)

    # share of assets that goes to the female
    # this has many repetative values but it turns out it does not matter much

    fem_gets = VecOnGrid(np.array(shrs), share_fem)
    mal_gets = VecOnGrid(np.array(shrs), share_mal)

    i_fem = fem_gets.i
    wn_fem = fem_gets.wnext

    i_mal = mal_gets.i
    wn_mal = mal_gets.wnext

    inds_exo = np.arange(setup.pars['nexo_t'][t + 1])

    if len(Vfemale[:, 0]) > 1:
        Vf_divorce = (1-wn_fem[None,:])*Vf_divorce_M[:,inds_exo,i_fem] + \
                    wn_fem[None,:]*Vf_divorce_M[:,inds_exo,i_fem+1]

        Vm_divorce = (1-wn_mal[None,:])*Vm_divorce_M[:,inds_exo,i_mal] + \
                    wn_mal[None,:]*Vm_divorce_M[:,inds_exo,i_mal+1]
    else:
        Vf_divorce = Vf_divorce_M[0, :, 0]
        Vm_divorce = Vm_divorce_M[0, :, 0]

    return Vf_divorce, Vm_divorce
def v_div_vartheta(setup,dc,t,sc,Vmale,Vfemale,izf,izm,
                   cost_fem=0.0,cost_mal=0.0, fun=lambda x : (x,1-x) ):
    # this produces value of divorce for gridpoints given possibly different
    # shares of how assets are divided. 
    # Returns Vf_divorce, Vm_divorce -- values of singles in case of divorce
    # matched to the gridpionts for couples
    
    # optional cost_fem and cost_mal are monetary costs of divorce
    
    
    shrs = setup.thetagrid
    
    # these are interpolation points
    
    Vm_divorce_M, Vf_divorce_M = v_div_allsplits(setup,dc,t,sc,
                                                 Vmale,Vfemale,izm,izf,
                                shrs=shrs,cost_fem=cost_fem,cost_mal=cost_mal)
    
    # share of assets that goes to the female
    # this has many repetative values but it turns out it does not matter much
    
    
    share_fem, share_mal = fun(setup.thetagrid)
    fem_gets = VecOnGrid(np.array(shrs),share_fem)
    mal_gets = VecOnGrid(np.array(shrs),share_mal)
    
    i_fem = fem_gets.i
    wn_fem = fem_gets.wnext
    wt_fem = setup.dtype(1) - wn_fem
    
    i_mal = mal_gets.i
    wn_mal = mal_gets.wnext
    wt_mal = setup.dtype(1) - wn_mal
    
    
    Vf_divorce = wt_fem[None,None,:]*Vf_divorce_M[:,:,i_fem] + \
                     wn_fem[None,None,:]*Vf_divorce_M[:,:,i_fem+1]
    
    Vm_divorce = wt_mal[None,None,:]*Vm_divorce_M[:,:,i_mal] + \
                     wn_mal[None,None,:]*Vm_divorce_M[:,:,i_mal+1]
                
    
    assert Vf_divorce.dtype == setup.dtype
    
    return Vf_divorce, Vm_divorce
Example #5
0
def v_div_allsplits(setup,
                    dc,
                    t,
                    sc,
                    Vmale,
                    Vfemale,
                    izm,
                    izf,
                    shrs=None,
                    cost_fem=0.0,
                    cost_mal=0.0):
    if shrs is None: shrs = shrs_def  # grid on possible assets divisions
    if len(Vmale[:, 0]) < 2: shrs = [0.5]
    shp = (sc.size, izm.size, len(shrs))
    Vm_divorce_M = np.zeros(shp)
    Vf_divorce_M = np.zeros(shp)

    # find utilities of divorce for different divisions of assets
    if len(Vmale[:, 0]) > 2:
        for i, shr in enumerate(shrs):
            sv_m = VecOnGrid(setup.agrid_s, shr * sc - cost_mal)
            sv_f = VecOnGrid(setup.agrid_s, shr * sc - cost_fem)

            Vm_divorce_M[..., i] = sv_m.apply(
                Vmale, axis=0, take=(1, izm), reshape_i=True) - dc.u_lost_m
            Vf_divorce_M[..., i] = sv_f.apply(
                Vfemale, axis=0, take=(1, izf), reshape_i=True) - dc.u_lost_f
    else:
        Vm_divorce_M[..., 0] = Vmale[:, izm] - dc.u_lost_m
        Vf_divorce_M[..., 0] = Vfemale[:, izf] - dc.u_lost_f

    return Vm_divorce_M, Vf_divorce_M
Example #6
0
def v_div_allsplits(setup,
                    dc,
                    t,
                    sc,
                    Vmale,
                    Vfemale,
                    izm,
                    izf,
                    shrs=None,
                    cost_fem=0.0,
                    cost_mal=0.0):

    if shrs is None: shrs = shrs_def  # grid on possible assets divisions
    shp = (sc.size, izm.size, len(shrs))
    Vm_divorce_M = np.zeros(shp)
    Vf_divorce_M = np.zeros(shp)

    # !!! potential problem is that when cupy is forced grids get converted many times

    # find utilities of divorce for different divisions of assets
    for i, shr in enumerate(shrs):
        sv_m = VecOnGrid(setup.agrid_s, shr * sc - cost_mal, force_cupy=gpu)
        sv_f = VecOnGrid(setup.agrid_s, shr * sc - cost_fem, force_cupy=gpu)

        Vm_divorce_M[..., i] = sv_m.apply(
            Vmale, axis=0, take=(1, izm), reshape_i=True) - dc.u_lost_m
        Vf_divorce_M[..., i] = sv_f.apply(
            Vfemale, axis=0, take=(1, izf), reshape_i=True) - dc.u_lost_f

    return Vm_divorce_M, Vf_divorce_M
Example #7
0
    def get_theta_grid(self):

        self.theta_grid = dict()
        tg = self.theta_grid
        ntheta = 11
        ntheta_fine = 121  # preliminary
        theta_min = 0.01
        theta_max = 0.99

        tg['theta_grid_coarse'] = np.linspace(theta_min, theta_max, ntheta)
        tg['ntheta_coarse'] = ntheta

        tfine = np.unique(
            np.concatenate(
                (np.linspace(theta_min, theta_max,
                             ntheta_fine), tg['theta_grid_coarse'])))

        tg['theta_gird_fine'] = tfine
        tg['ntheta_fine'] = tfine.size
        tg['v_theta'] = VecOnGrid(tg['theta_grid_coarse'],
                                  tg['theta_gird_fine'])
Example #8
0
def v_mar_igrid(setup,
                t,
                V,
                icouple,
                ind_or_inds,
                *,
                female,
                giveabirth,
                uloss_fem,
                uloss_mal,
                uloss_fem_single=0.0,
                uloss_mal_single=0.0,
                unplanned_pregnancy=False,
                interpolate=True,
                return_all=False):
    # this returns value functions for couple that entered the last period with
    # (s,Z,theta) from the grid and is allowed to renegotiate them or breakup

    if giveabirth:
        coup = 'Couple and child'
    else:
        coup = 'Couple, no children'

    p_access, abortion_costs = setup.pars['p_abortion_access'], setup.pars[
        'abortion_costs']

    if unplanned_pregnancy:
        assert giveabirth
        Vfem = (1-p_access)*V['Female and child']['V'] + \
                p_access*np.maximum(V['Female and child']['V'],V['Female, single']['V']-abortion_costs)
    else:
        Vfem = V['Female, single']['V']

    # import objects
    agrid_c = setup.agrid_c
    agrid_s = setup.agrid_s
    gamma = setup.pars['m_bargaining_weight']

    VMval_single, VFval_single = V['Male, single'][
        'V'] - uloss_mal_single, Vfem - uloss_fem_single
    VMval_postren, VFval_postren = V[coup]['VM'] - uloss_mal, V[coup][
        'VF'] - uloss_fem

    # unify dimensions

    # substantial part
    ind, izf, izm, ipsi = setup.all_indices(t, ind_or_inds)

    #sc = sf+sm # savings of couple
    s_partner = agrid_c[icouple] - agrid_s  # we assume all points on grid

    # this implicitly trims negative or too large values
    s_partner_v = VecOnGrid(agrid_s, s_partner, trim=True)

    # this applies them

    if female:
        Vfs = VFval_single[:, izf]
        Vms = s_partner_v.apply(VMval_single, axis=0, take=(1, izm))
    else:
        Vms = VMval_single[:, izm]
        Vfs = s_partner_v.apply(VFval_single, axis=0, take=(1, izf))

    do_abortion = ((V['Female, single']['V'][:, izf] - abortion_costs) >=
                   V['Female and child']['V'][:, izf])

    expnd = lambda x: setup.v_thetagrid_fine.apply(x, axis=2)

    Vmm, Vfm = (expnd(x[:, ind, :]) for x in (VMval_postren, VFval_postren))

    ins = [Vfm, Vmm, Vfs, Vms, gamma]
    ins = [setup.dtype(x, copy=False) for x in ins]  # optional type conversion
    vfout, vmout, nbsout, agree, ithetaout = mar_mat(*ins)

    if not return_all:
        return {
            'Values': (vfout, vmout),
            'NBS': nbsout,
            'theta': ithetaout,
            'Decision': agree,
            'Abortion': do_abortion
        }
    else:
        return {
            'Values': (vfout, vmout),
            'NBS': nbsout,
            'theta': ithetaout,
            'Decision': agree,
            'Abortion': do_abortion,
            'ins': ins
        }
Example #9
0
    def __init__(self,nogrid=False,divorce_costs='Default',separation_costs='Default',**kwargs): 
        p = dict()       
        period_year=1#this can be 1,2,3 or 6
        transform=2#this tells how many periods to pull together for duration moments
        T = int(62/period_year)
        Tret = int(47/period_year) # first period when the agent is retired
        Tbef=int(2/period_year)
        Tren  = int(47/period_year)#int(42/period_year) # period starting which people do not renegotiate/divroce
        Tmeet = int(47/period_year)#int(42/period_year) # period starting which you do not meet anyone
        p['py']=period_year
        p['ty']=transform
        p['T'] = T
        p['Tret'] = Tret
        p['Tren'] = Tren
        p['Tbef'] = Tbef
        p['sig_zf_0']  = 0.5449176#0.4096**(0.5)
        p['sig_zf']    = .0272437**(0.5)#0.0399528**(0.5)
        p['n_zf_t']      = [5]*Tret + [1]*(T-Tret)
        p['sig_zm_0']  = 0.54896510#.405769**(0.5)
        p['sig_zm']    = .025014**(0.5)#0.0417483**(0.5)
        p['n_zm_t']      = [5]*Tret + [1]*(T-Tret)
        p['sigma_psi_mult'] = 0.28
        p['sigma_psi']   = 0.11
        p['n_psi_t']     = [11]*T
        p['R_t'] = [1.02**period_year]*T
        p['beta_t'] = [0.98**period_year]*T
        p['A'] = 1.0 # consumption in couple: c = (1/A)*[c_f^(1+rho) + c_m^(1+rho)]^(1/(1+rho))
        p['crra_power'] = 1.5
        p['couple_rts'] = 0.0 
        p['sig_partner_a'] = 0.1#0.5
        p['sig_partner_z'] = 1.2#1.0#0.4 #This is crazy powerful for the diff in diff estimate
        p['sig_partner_mult'] = 1.0
        p['dump_factor_z'] = 0.85#0.82
        p['mean_partner_z_female'] = 0.02#+0.03
        p['mean_partner_z_male'] =  -0.02#-0.03
        p['mean_partner_a_female'] = 0.0#0.1
        p['mean_partner_a_male'] = 0.0#-0.1
        p['m_bargaining_weight'] = 0.5
        p['pmeet'] = 0.5
        
        p['z_drift'] = -0.09#-0.1
        
        
        p['wage_gap'] = 0.6
        p['wret'] = 0.6#0.5
        p['uls'] = 0.2
        p['pls'] = 1.0
        
        
        
        p['u_shift_mar'] = 0.0
        p['u_shift_coh'] = -0.00
        
         
        #Wages over time
        p['f_wage_trend'] = [0.0*(t>=Tret)+(t<Tret)*(-.74491918 +.04258303*t -.0016542*t**2+.00001775*t**3) for t in range(T)]
        p['f_wage_trend_single'] = [0.0*(t>=Tret)+(t<Tret)*(-.6805060 +.04629912*t -.00160467*t**2+.00001626*t**3) for t in range(T)]
        p['m_wage_trend'] = [0.0*(t>=Tret)+(t<Tret)*(-0.5620125  +0.06767721*t -0.00192571*t**2+ 0.00001573*t**3) for t in range(T)]
        p['m_wage_trend_single'] = [0.0*(t>=Tret)+(t<Tret)*(-.5960803  +.05829568*t -.00169143*t**2+ .00001446*t**3) for t in range(T)]
   

#        p['f_wage_trend'] = [(-0.5620125  +0.06767721*t -0.00192571*t**2+ 0.00001573*t**3) for t in range(T)]
#        p['f_wage_trend_single'] =  [(-.5960803  +.05829568*t -.00169143*t**2+ .00001446*t**3) for t in range(T)]
#        p['m_wage_trend'] = [(-0.5620125  +0.06767721*t -0.00192571*t**2+ 0.00001573*t**3) for t in range(T)]
#        p['m_wage_trend_single'] = [(-.5960803  +.05829568*t -.00169143*t**2+ .00001446*t**3) for t in range(T)]
           
  
        p['util_lam'] = 0.19#0.4
        p['util_alp'] = 0.5
        p['util_xi'] = 1.07
        p['util_kap'] = (1-0.21)/(0.21)
        p['rprice_durables'] = 1.0#
        

        
        for key, value in kwargs.items():
            assert (key in p), 'wrong name?'
            p[key] = value
            
        #Adjust kappa and alpha to make sense of relative prices
        p['util_alp_m']=p['util_alp']*(1.0/(p['rprice_durables'])**(1.0-p['util_xi']))
        p['util_kap_m']=p['util_kap']*p['rprice_durables']**p['util_lam']
            
            
        # no replacements after this pint     
        p['sigma_psi_init'] = p['sigma_psi_mult']*p['sigma_psi']
        
        #Get the probability of meeting, adjusting for year-period
        p_meet=p['pmeet']
        for j in range(period_year-1):
            p_meet=p_meet+(1-p_meet)*p['pmeet']
            
            
        # timing here    
            
        p['pmeet_t'] = [p_meet]*Tmeet + [0.0]*(T-Tmeet)
        p['can divorce'] = [True]*Tren + [False]*(T-Tren)
        
        
        self.pars = p
        
        self.dtype = np.float64 # type for all floats
        
       
        
        # relevant for integration
        self.state_names = ['Female, single','Male, single','Couple, M', 'Couple, C']
        
        # female labor supply
        self.ls_levels = np.array([0.0,0.8],dtype=self.dtype)
        self.mlevel=0.8
        #self.ls_utilities = np.array([p['uls'],0.0],dtype=self.dtype)
        self.ls_pdown = np.array([p['pls'],0.0],dtype=self.dtype)
        self.nls = len(self.ls_levels)
        
        
        
        
        
        #Cost of Divorce
        if divorce_costs == 'Default':
            # by default the costs are set in the bottom
            self.div_costs = DivorceCosts(eq_split=1.0,assets_kept=1.0)
        else:
            if isinstance(divorce_costs,dict):
                # you can feed in arguments to DivorceCosts
                self.div_costs = DivorceCosts(**divorce_costs)
            else:
                # or just the output of DivorceCosts
                assert isinstance(divorce_costs,DivorceCosts)
                self.div_costs = divorce_costs
                
        #Cost of Separation
        if separation_costs == 'Default':
            # by default the costs are set in the bottom
            self.sep_costs = DivorceCosts(eq_split=0.0,assets_kept=1.0)
        else:
            if isinstance(separation_costs,dict):
                # you can feed in arguments to DivorceCosts
                self.sep_costs = DivorceCosts(**separation_costs)
            else:
                # or just the output of DivorceCosts
                assert isinstance(separation_costs,DivorceCosts)
                self.sep_costs = separation_costs
            
        # exogrid should be deprecated
        if not nogrid:
        
            exogrid = dict()
            
            
            # let's approximate three Markov chains
            # this sets up exogenous grid
            
            # FIXME: this uses number of points from 0th entry. 
            # in principle we can generalize this
            
            p['n_zf_t']      = [5]*Tret + [5]*(T-Tret)
            p['n_zm_t']      = [5]*Tret + [5]*(T-Tret)
            exogrid['zf_t'],  exogrid['zf_t_mat'] = rouw_nonst(p['T'],p['sig_zf']*period_year**0.5,p['sig_zf_0'],p['n_zf_t'][0])
            exogrid['zm_t'],  exogrid['zm_t_mat'] = rouw_nonst(p['T'],p['sig_zm']*period_year**0.5,p['sig_zm_0'],p['n_zm_t'][0])
            
            ################################
            #First mimic US pension system
            ###############################
            

            
            #function to compute pension
            def pens(value):
                
                #Get median income before retirement using men model income in Tret-1
                #+ ratio of men and female labor income for the rest
                yret=(1.73377+(.8427056/1.224638)*1.73377* 0.3246206)/(1+0.3246206)
                thresh1=0.38*yret
                thresh2=1.59*yret
                
                inc1=np.minimum(value,thresh1)
                inc2=np.maximum(np.minimum(value-inc1,thresh2-inc1),0)
                inc3=np.maximum(value-thresh2,0)
                
                return inc1*0.9+inc2*0.32+inc3*0.15
                
              
            
            for t in range(Tret,T):
                exogrid['zf_t'][t] = np.array([np.log(p['wret'])])
                exogrid['zm_t'][t] = np.array([np.log(p['wret'])])
                exogrid['zf_t_mat'][t] = np.atleast_2d(1.0)
                exogrid['zm_t_mat'][t] = np.atleast_2d(1.0)
                
                
            # fix transition from non-retired to retired    
            exogrid['zf_t_mat'][Tret-1] = np.ones((p['n_zf_t'][Tret-1],1))
            exogrid['zm_t_mat'][Tret-1] = np.ones((p['n_zm_t'][Tret-1],1))
            
            #Tax system as in Wu and Kruger
            for t in range(0,Tret):
                exogrid['zf_t'][t] = exogrid['zf_t'][t]#*(1-0.1327)+np.log(1-0.1575)
                exogrid['zm_t'][t] = exogrid['zm_t'][t]#*(1-0.1327)+np.log(1-0.1575)  
            
            #Comment out the following if you dont want retirment based on income
            for t in range(Tret,T):
                #exogrid['zf_t'][t] = np.log(p['wret']*np.exp(p['f_wage_trend'][Tret-1]+exogrid['zf_t'][Tret-1]))#np.array([np.log(p['wret'])])
                #exogrid['zm_t'][t] = np.log(p['wret']*np.exp(p['m_wage_trend'][Tret-1]+exogrid['zm_t'][Tret-1]))
                exogrid['zf_t'][t] = np.log(pens(np.exp(p['f_wage_trend'][Tret-1]+exogrid['zf_t'][Tret-1])))#np.array([np.log(p['wret'])])
                exogrid['zm_t'][t] = np.log(pens(np.exp(p['m_wage_trend'][Tret-1]+exogrid['zm_t'][Tret-1])))               
                exogrid['zf_t_mat'][t] = np.diag(np.ones(len(exogrid['zf_t'][t])))#p.atleast_2d(1.0)
                exogrid['zm_t_mat'][t] = np.diag(np.ones(len(exogrid['zm_t'][t])))
                
            # fix transition from non-retired to retired    
            exogrid['zf_t_mat'][Tret-1] = np.diag(np.ones(len(exogrid['zf_t'][Tret-1])))
            exogrid['zm_t_mat'][Tret-1] = np.diag(np.ones(len(exogrid['zm_t'][Tret-1])))


            
            exogrid['psi_t'], exogrid['psi_t_mat'] = tauchen_nonst(p['T'],p['sigma_psi']*period_year**0.5,p['sigma_psi_init'],p['n_psi_t'][0])
            exogrid['psi_t_mat'][Tret-1] = np.diag(np.ones(len(exogrid['psi_t_mat'][Tret-1])))
            exogrid['psi_t_mat'][Tret] = np.diag(np.ones(len(exogrid['psi_t_mat'][Tret-1])))
            exogrid['psi_t_mat'][Tret+1] = np.diag(np.ones(len(exogrid['psi_t_mat'][Tret-1])))
            
            #Here I impose no change in psi from retirement till the end of time 
           # for t in range(Tret,T-1):
               
             #   exogrid['psi_t'][t] = exogrid['psi_t'][Tret-1]#np.array([np.log(p['wret'])])             
             #   exogrid['psi_t_mat'][t] = np.diag(np.ones(len(exogrid['psi_t'][t])))#p.atleast_2d(1.0)

                
            
            zfzm, zfzmmat = combine_matrices_two_lists(exogrid['zf_t'], exogrid['zm_t'], exogrid['zf_t_mat'], exogrid['zm_t_mat'])
            all_t, all_t_mat = combine_matrices_two_lists(zfzm,exogrid['psi_t'],zfzmmat,exogrid['psi_t_mat'])
            all_t_mat_sparse_T = [sparse.csc_matrix(D.T) if D is not None else None for D in all_t_mat]
            
            
            #Create a new bad version of transition matrix p(zf_t)
            
            
            zf_bad = [tauchen_drift(exogrid['zf_t'][t], exogrid['zf_t'][t+1], 
                                    1.0, p['sig_zf'], p['z_drift'])
                        for t in range(self.pars['Tret']-1) ]
            
            #Account for retirement here
            zf_bad = zf_bad+[exogrid['zf_t_mat'][t] for t in range(self.pars['Tret']-1,self.pars['T']-1)]+ [None]
            
            zf_t_mat_down = zf_bad
            
            zfzm, zfzmmat = combine_matrices_two_lists(exogrid['zf_t'], exogrid['zm_t'], zf_t_mat_down, exogrid['zm_t_mat'])
            all_t_down, all_t_mat_down = combine_matrices_two_lists(zfzm,exogrid['psi_t'],zfzmmat,exogrid['psi_t_mat'])
            all_t_mat_down_sparse_T = [sparse.csc_matrix(D.T) if D is not None else None for D in all_t_mat_down]
            
            
            
            all_t_mat_by_l = [ [(1-p)*m + p*md if m is not None else None 
                                for m , md in zip(all_t_mat,all_t_mat_down)]
                               for p in self.ls_pdown ]
            
            all_t_mat_by_l_spt = [ [(1-p)*m + p*md if m is not None else None
                                    for m, md in zip(all_t_mat_sparse_T,all_t_mat_down_sparse_T)]
                               for p in self.ls_pdown ]
            
            
            
            exogrid['all_t_mat_by_l'] = all_t_mat_by_l
            exogrid['all_t_mat_by_l_spt'] = all_t_mat_by_l_spt
            
            exogrid['all_t'] = all_t
            
            Exogrid_nt = namedtuple('Exogrid_nt',exogrid.keys())
            
            self.exogrid = Exogrid_nt(**exogrid)
            self.pars['nexo_t'] = [v.shape[0] for v in all_t]
            
            #assert False
            
            
            
        #Grid Couple
        self.na = 40#40
        self.amin = 0
        self.amax = 80
        self.amax1 = 180
        self.agrid_c = np.linspace(self.amin,self.amax,self.na,dtype=self.dtype)
        tune=2.5
        self.agrid_c = np.geomspace(self.amin+tune,self.amax+tune,num=self.na)-tune
        self.agrid_c[-1]=self.amax1
        self.agrid_c[-2]=120
        # this builds finer grid for potential savings
        s_between = 7 # default numer of points between poitns on agrid
        s_da_min = 0.1 # minimal step (does not create more points)
        s_da_max = 1.0 # maximal step (creates more if not enough)
        
        self.sgrid_c = build_s_grid(self.agrid_c,s_between,s_da_min,s_da_max)
        self.vsgrid_c = VecOnGrid(self.agrid_c,self.sgrid_c)
        
        
         
        #Grid Single
        scale = 1.1
        self.amin_s = 0
        self.amax_s = self.amax/scale
        self.agrid_s = np.linspace(self.amin_s,self.amax_s,self.na,dtype=self.dtype)
        #self.agrid_s[self.na-1]=18#180
        tune_s=2.5
        self.agrid_s = np.geomspace(self.amin_s+tune_s,self.amax_s+tune_s,num=self.na)-tune_s
        self.agrid_s[-1]=self.amax1/scale
        self.agrid_c[-2]=120/scale
        self.sgrid_s = build_s_grid(self.agrid_s,s_between,s_da_min,s_da_max)
        self.vsgrid_s = VecOnGrid(self.agrid_s,self.sgrid_s)
        
        # grid for theta
        self.ntheta = 11
        self.thetamin = 0.02
        self.thetamax = 0.98
        self.thetagrid = np.linspace(self.thetamin,self.thetamax,self.ntheta,dtype=self.dtype)
        
        
        
        
        
        
        # construct finer grid for bargaining
        ntheta_fine = 5*self.ntheta # actual number may be a bit bigger
        self.thetagrid_fine = np.unique(np.concatenate( (self.thetagrid,np.linspace(self.thetamin,self.thetamax,ntheta_fine,dtype=self.dtype)) ))
        self.ntheta_fine = self.thetagrid_fine.size
        
        i_orig = list()
        
        for theta in self.thetagrid:
            assert theta in self.thetagrid_fine
            i_orig.append(np.where(self.thetagrid_fine==theta)[0])
            
        assert len(i_orig) == self.thetagrid.size
        # allows to recover original gird points on the fine grid        
        self.theta_orig_on_fine = np.array(i_orig).flatten()
        self.v_thetagrid_fine = VecOnGrid(self.thetagrid,self.thetagrid_fine)
        # precomputed object for interpolation
        
        #Get indexes from fine back to coarse thetagrid
        cg=VecOnGrid(self.thetagrid,self.thetagrid_fine)
        index_t=cg.i
        index_t1=index_t+1
        wherep=(cg.wthis<0.5)
        self.igridcoarse=index_t
        self.igridcoarse[wherep]=index_t1[wherep]

            
        
        
        


        self.exo_grids = {'Female, single':exogrid['zf_t'],
                          'Male, single':exogrid['zm_t'],
                          'Couple, M':exogrid['all_t'],
                          'Couple, C':exogrid['all_t']}
        self.exo_mats = {'Female, single':exogrid['zf_t_mat'],
                          'Male, single':exogrid['zm_t_mat'],
                          'Couple, M':exogrid['all_t_mat_by_l'],
                          'Couple, C':exogrid['all_t_mat_by_l']} # sparse version?
        
        
        self.utility_shifters = {'Female, single':0.0,
                                 'Male, single':0.0,
                                 'Couple, M':p['u_shift_mar'],
                                 'Couple, C':p['u_shift_coh']}
        
        
        # this pre-computes transition matrices for meeting a partner
        zf_t_partmat = [self.mar_mats_iexo(t,female=True) if t < p['T'] - 1 else None 
                            for t in range(p['T'])]
        zm_t_partmat = [self.mar_mats_iexo(t,female=False) if t < p['T'] - 1 else None 
                            for t in range(p['T'])]
        
        self.part_mats = {'Female, single':zf_t_partmat,
                          'Male, single':  zm_t_partmat,
                          'Couple, M': None,
                          'Couple, C': None} # last is added for consistency
        
        self.mar_mats_assets()
        
        self.mar_mats_combine()
        
        
        # building m grid
        ezfmin = min([np.min(self.ls_levels[-1]*np.exp(g+t)) for g,t in zip(exogrid['zf_t'],p['f_wage_trend'])])
        ezmmin = min([np.min(self.mlevel*np.exp(g+t)) for g,t in zip(exogrid['zm_t'],p['m_wage_trend'])])
        ezfmax = max([np.max(self.ls_levels[-1]*np.exp(g+t)) for g,t in zip(exogrid['zf_t'],p['f_wage_trend'])])
        ezmmax = max([np.max(self.mlevel*np.exp(g+t)) for g,t in zip(exogrid['zm_t'],p['m_wage_trend'])])
        
        
        
        self.money_min = 0.95*min(ezmmin,ezfmin) # cause FLS can be up to 0
        #self.mgrid = ezmmin + self.sgrid_c # this can be changed later
        mmin = self.money_min
        mmax = ezfmax + ezmmax + np.max(self.pars['R_t'])*self.amax1
        mint = (ezfmax + ezmmax) # poin where more dense grid begins
        
        ndense = 600
        nm = 1500
        
        gsparse = np.linspace(mint,mmax,nm-ndense)
        gdense = np.linspace(mmin,mint,ndense+1) # +1 as there is a common pt
        
        self.mgrid = np.zeros(nm,dtype=self.dtype)
        self.mgrid[ndense:] = gsparse
        self.mgrid[:(ndense+1)] = gdense
        assert np.all(np.diff(self.mgrid)>0)
        
        self.u_precompute()
Example #10
0
def v_mar(setup,
          V,
          t,
          iassets_couple,
          iexo_couple,
          *,
          match_type,
          female,
          return_insides=False,
          nogpu_here=nogpu):

    # this builds matrix for all matches specified by grid positions
    # iassets_couple (na X nmatches) and iexo_couple (nmatches)

    iexo, izf, izm, ipsi = [co(x) for x in setup.all_indices(t, iexo_couple)]

    vals = pick_values(setup, V, match_type=match_type)
    V_fem, V_mal = vals['V_fem'], vals['V_mal']

    agrid_c = setup.agrid_c if nogpu else setup.cupy.agrid_c
    agrid_s = setup.agrid_s if nogpu else setup.cupy.agrid_s

    # obtain partner's value

    assets_partner = np.clip(agrid_c[iassets_couple] - agrid_s[:, None], 0.0,
                             agrid_s.max())
    # this can be fastened by going over repeated values of ipsi
    v_assets_partner = VecOnGrid(agrid_s, assets_partner)
    i, wnext, wthis = v_assets_partner.i, v_assets_partner.wnext, v_assets_partner.wthis

    if type(V_fem) is tuple:
        # in this case
        assert match_type == 'Unplanned pregnancy'
        V_f_no, V_m_no, abortion_decision = get_upp_disagreement_values(
            setup, t, V_fem, V_mal, izf, izm, female, v_assets_partner)
    else:
        if female:
            V_f_no = V_fem[:, izf]
            V_m_no = V_mal[i, izm[None, :]] * wthis + V_mal[i + 1, izm[
                None, :]] * wnext
        else:
            V_f_no = V_fem[i, izf[None, :]] * wthis + V_fem[i + 1, izf[
                None, :]] * wnext
            V_m_no = V_mal[:, izm]

    V_fem_mar, V_mal_mar = vals['V_fem_mar'], vals['V_mal_mar']

    ia = iassets_couple

    V_f_yes = V_fem_mar[ia, iexo[None, :], :]
    V_m_yes = V_mal_mar[ia, iexo[None, :], :]

    assert V_f_yes.shape[:-1] == V_f_no.shape
    assert V_m_yes.shape[:-1] == V_m_no.shape

    # fill abortion decisions
    if match_type == 'Unplanned pregnancy' and female:
        do_abortion = abortion_decision  #vals['i_abortion'][:,izf]
    else:
        do_abortion = np.zeros(V_f_no.shape, dtype=np.bool_)

    if nogpu:
        it, wnt = setup.v_thetagrid_fine.i, setup.v_thetagrid_fine.wnext
    else:
        it, wnt = setup.cupy.v_thetagrid_fine.i, setup.cupy.v_thetagrid_fine.wnext

    if nogpu:
        v_f, v_m, agree, nbs, itheta = get_marriage_values(
            V_f_yes, V_m_yes, V_f_no, V_m_no, it, wnt)
    else:
        v_f, v_m, agree, nbs, itheta = v_mar_gpu(V_f_yes, V_m_yes, V_f_no,
                                                 V_m_no, it, wnt)

    out = {
        'V_fem': v_f,
        'V_mal': v_m,
        'Agree': agree,
        'NBS': nbs,
        'itheta': itheta,
        'Abortion': do_abortion
    }

    if return_insides:
        out.update({
            'V_f_yes': V_f_yes,
            'V_m_yes': V_m_yes,
            'V_f_no': V_f_no,
            'V_m_no': V_m_no
        })

    return out
Example #11
0
def v_div_byshare(model,
                  dc,
                  t,
                  sc,
                  share_fem,
                  share_mal,
                  Vmale,
                  Vfemale,
                  izf,
                  izm,
                  shrs=None,
                  cost_fem=0.0,
                  cost_mal=0.0):
    # this produces value of divorce for gridpoints given possibly different
    # shares of how assets are divided.
    # Returns Vf_divorce, Vm_divorce -- values of singles in case of divorce
    # matched to the gridpionts for couples

    setup = model.setup

    # optional cost_fem and cost_mal are monetary costs of divorce
    if shrs is None: shrs = shrs_def

    # there is simpler protocol if shares are fixed

    def ptp(x):  # not implemented in cupy somehow
        return x.max() - x.min()

    simple = False
    if (ptp(share_fem) < 1e-4) and (ptp(share_mal) < 1e-4):
        simple = True

        shr_m = share_mal[0]

        agrid_s = setup.agrid_s if not gpu else setup.cupy.agrid_s

        sv_m = VecOnGrid(agrid_s, shr_m * sc - cost_mal, force_cupy=gpu)

        shr_f = share_fem[0]

        if np.abs(shr_f - shr_m) < 1e-4:
            sv_f = sv_m
        else:
            sv_f = VecOnGrid(agrid_s, shr_f * sc - cost_fem, force_cupy=gpu)

        def apply(v, sv, iexo):
            i = sv.i[:, None]
            ip = i + 1
            wt = sv.wthis[:, None]
            wn = sv.wnext[:, None]
            ie = iexo[None, :]
            return v[i, ie] * wt + v[ip, ie] * wn

        Vf_divorce = apply(Vfemale, sv_f, izf) - dc.u_lost_f
        Vm_divorce = apply(Vmale, sv_m, izm) - dc.u_lost_m

        return Vf_divorce, Vm_divorce

    Vm_divorce_M, Vf_divorce_M = v_div_allsplits(setup,
                                                 dc,
                                                 t,
                                                 sc,
                                                 Vmale,
                                                 Vfemale,
                                                 izm,
                                                 izf,
                                                 shrs=shrs,
                                                 cost_fem=cost_fem,
                                                 cost_mal=cost_mal)

    # share of assets that goes to the female
    # this has many repetative values but it turns out it does not matter much

    fem_gets = VecOnGrid(np.array(shrs), share_fem, force_cupy=gpu)
    mal_gets = VecOnGrid(np.array(shrs), share_mal, force_cupy=gpu)

    i_fem = fem_gets.i
    wn_fem = fem_gets.wnext

    i_mal = mal_gets.i
    wn_mal = mal_gets.wnext

    inds_exo = np.arange(setup.pars['nexo_t'][t + 1])



    Vf_divorce = (1-wn_fem[None,:])*Vf_divorce_M[:,inds_exo,i_fem] + \
                wn_fem[None,:]*Vf_divorce_M[:,inds_exo,i_fem+1]

    Vm_divorce = (1-wn_mal[None,:])*Vm_divorce_M[:,inds_exo,i_mal] + \
                wn_mal[None,:]*Vm_divorce_M[:,inds_exo,i_mal+1]

    return Vf_divorce, Vm_divorce
Example #12
0
    def statenext(self, t):

        for ipol in range(self.npol):
            for ist, sname in enumerate(self.state_names):
                is_state_any_pol = (self.state[:, t] == ist)
                is_pol = (self.policy_ind[:, t] == ipol)
                is_state = (is_state_any_pol) & (is_pol)

                if not np.any(is_state):
                    continue

                if self.verbose:
                    print('At t = {} count of {} is {}'.format(
                        t + 1, sname, np.sum(is_state)))

                ind = np.where(is_state)[0]

                nind = ind.size

                if sname == "Female, single" or sname == "Male, single" or sname == "Female and child":
                    # TODO: this is temporary version, it computes partners for
                    # everyone and after that imposes meet / no meet, this should
                    # not happen.

                    # meet a partner

                    ss = self.single_state
                    ss_sm = 'Female and child' if self.female else self.single_state

                    pcoef = self.Mlist[ipol].setup.pars['pmeet_multiplier_fem']

                    pmeet = pcoef * self.Mlist[ipol].setup.pars['pmeet_t'][
                        t]  # TODO: check timing
                    p_abortion_access = self.Mlist[ipol].setup.pars[
                        'p_abortion_access']

                    # divide by 2 subgroups

                    matches = self.Mlist[ipol].decisions[t][sname]

                    ia = self.iassets[
                        ind,
                        t + 1]  # note that timing is slightly inconsistent

                    # we use iexo from t and savings from t+1
                    # TODO: fix the seed
                    iznow = self.iexo[ind, t]

                    if not 'Not pregnant' in matches or (pmeet < 1e-5):
                        assert pmeet < 1e-5
                        # propagate yaftmar
                        # !!! this needs to be verified

                        # iexo is set within iexonext
                        izf = self.iexo[ind, t + 1]
                        self.yaftmar[ind,t+1] = \
                            (self.yaftmar[ind,t] + 1)*(self.yaftmar[ind,t]>=0)+(-1)*(self.yaftmar[ind,t]<0)

                        if sname != "Female and child":
                            self.ils_i[ind, t + 1] = self.ils_def
                            self.state[ind, t + 1] = self.state_codes[ss]
                        else:
                            fls_policy = self.Mlist[ipol].V[
                                t + 1]['Female and child']['fls']
                            self.state[
                                ind,
                                t + 1] = self.state_codes['Female and child']
                            self.ils_i[ind,
                                       t + 1] = fls_policy[self.iassets[ind,
                                                                        t + 1],
                                                           izf]
                        continue

                    pmat_np = matches['Not pregnant']['p_mat_extended'][
                        iznow, :]  # assuming it is identical !!!
                    pmat_cum_np = pmat_np.cumsum(axis=1)
                    pmat_cum_p = pmat_cum_np

                    assert np.all(pmat_cum_np < 1 + 1e-5)
                    assert np.all(pmat_cum_np[:, -1] > 1 - 1e-5)

                    assert np.all(pmat_cum_p < 1 + 1e-5)
                    assert np.all(pmat_cum_p[:, -1] > 1 - 1e-5)
                    assert np.all(pmat_cum_p >= 0.0)
                    # there is a rare numerical issue when adding lots of floats
                    # gives imprecise result > 1

                    pmat_cum_np[:, -1] = 1 + 1e-5
                    pmat_cum_p[:, -1] = 1 + 1e-5

                    v = self._shocks_single_iexo[
                        ind,
                        t]  #np.random.random_sample(ind.size) # draw uniform dist

                    i_pmat_np = (v[:, None] > pmat_cum_np).sum(
                        axis=1)  # index of the position in pmat
                    i_pmat_p = (v[:, None] > pmat_cum_p).sum(
                        axis=1)  # index of the position in pmat

                    # these things are the same for pregnant and not pregnant

                    if self.female:
                        fert = self.setup.pars['is fertile'][t]
                    else:
                        fert = self.setup.pars['is fertile'][
                            t - 2] if t >= 2 else False

                    if sname == 'Female, single':
                        p_preg = fert * self.setup.upp_precomputed_fem[t][
                            self.iexo[ind, t]]
                    elif sname == 'Male, single':
                        p_preg = fert * self.setup.upp_precomputed_mal[t][
                            self.iexo[ind, t]]
                    elif sname == 'Female and child':
                        p_preg = np.ones_like(ind, dtype=np.float64)
                    else:
                        assert False

                    # these are individual-specific pregnancy probabilities
                    # for those who are not fertile this is forced to be zero

                    # potential assets position of couple

                    vpreg = self._shocks_single_preg[ind, t]
                    i_preg = (vpreg < p_preg)

                    #i_pmat = i_pmat_p*(i_preg) + i_pmat_np*(~i_preg)


                    ic_out = matches['Not pregnant']['corresponding_iexo'][i_pmat_np]*(~i_preg) \
                                + matches['Pregnant']['corresponding_iexo'][i_pmat_p]*(i_preg)

                    imatch = matches['Not pregnant']['corresponding_imatch'][i_pmat_np]*(~i_preg) \
                                + matches['Pregnant']['corresponding_imatch'][i_pmat_p]*(i_preg)


                    ia_out = matches['Not pregnant']['ia_c_table'][ia,i_pmat_np]*(~i_preg) \
                                + matches['Pregnant']['ia_c_table'][ia,i_pmat_p]*(i_preg)

                    it_out = matches['Not pregnant']['itheta'][ia,i_pmat_np]*(~i_preg) + \
                                + matches['Pregnant']['itheta'][ia,i_pmat_p]*(i_preg)

                    iall, izf, izm, ipsi = self.Mlist[ipol].setup.all_indices(
                        t, ic_out)

                    # compute for everyone

                    vmeet = self._shocks_single_meet[ind, t]
                    i_nomeet = np.array(vmeet > pmeet)

                    v_abortion = self._shocks_outsm[ind, t]
                    i_abortion_allowed = np.array(
                        v_abortion < p_abortion_access)



                    i_pot_agree = matches['Not pregnant']['Decision'][ia,i_pmat_np]*(~i_preg) \
                                    + matches['Pregnant']['Decision'][ia,i_pmat_p]*(i_preg)

                    i_m_preferred = matches['Not pregnant']['Child immediately'][ia,i_pmat_np]*(~i_preg) \
                                    + matches['Pregnant']['Child immediately'][ia,i_pmat_p]*(i_preg)

                    i_abortion_preferred = matches['Not pregnant']['Abortion'][ia,i_pmat_np]*(~i_preg) \
                                    + matches['Pregnant']['Abortion'][ia,i_pmat_p]*(i_preg)

                    i_disagree = (~i_pot_agree)

                    i_disagree_or_nomeet = (i_disagree) | (i_nomeet)
                    i_disagree_and_meet = (i_disagree) & ~(i_nomeet)

                    i_abortion = (i_abortion_allowed) & (
                        i_abortion_preferred) & (i_disagree_and_meet) & (
                            i_preg) & (sname == 'Female, single')
                    i_kept = (i_abortion_allowed) & (~i_abortion_preferred) & (
                        i_disagree_and_meet) & (i_preg) & (sname
                                                           == 'Female, single')
                    i_no_access = ~(i_abortion_allowed) & (
                        i_disagree_and_meet) & (i_preg) & (sname
                                                           == 'Female, single')

                    self.disagreed[ind, t] = i_disagree_and_meet
                    self.met_a_partner[ind, t] = ~i_nomeet
                    self.unplanned_preg[ind, t] = (i_preg) & (
                        ~i_nomeet) & ~(sname == 'Female and child')
                    self.aborted[ind, t] = i_abortion

                    n_abortions = i_abortion.sum()
                    n_kept = i_kept.sum()
                    #if n_abortions>0: print('{} abortions done at t = {} for {}'.format(n_abortions,t,sname))
                    #if n_kept>0: print('{} abortions refused at t = {} for {}'.format(n_kept,t,sname))

                    if not sname == 'Female and child':
                        become_sm = (i_disagree_and_meet) & (
                            i_preg) & self.female & ~(i_abortion)
                        become_single = (i_disagree_or_nomeet) & ~(become_sm)
                    else:
                        become_sm = (i_disagree_or_nomeet) & (i_preg)
                        assert np.all(i_preg)
                        become_single = np.zeros_like(become_sm, dtype=np.bool)
                    assert np.all(i_disagree_or_nomeet == ((become_sm)
                                                           | (become_single)))

                    i_agree = ~i_disagree_or_nomeet

                    i_agree_mar = (i_agree) & (i_m_preferred)
                    i_agree_coh = (i_agree) & (~i_m_preferred)

                    self.agreed[ind, t] = (i_agree_mar) | (i_agree_coh)
                    self.planned_preg[ind, t] = (i_agree_mar) & ~(i_preg)

                    self.new_child[ind, t + 1] = (i_kept | i_no_access) | (
                        i_agree_mar & ~(sname == 'Female and child'))

                    assert np.all(~i_nomeet[i_agree])

                    i_firstmar = (self.nmar[ind, t] == 0)

                    nmar, ncoh, ndis, nnom = np.sum(i_agree_mar), np.sum(
                        i_agree_coh), np.sum(i_disagree_and_meet), np.sum(
                            i_nomeet)
                    ntot = sum((nmar, ncoh, ndis, nnom))

                    if self.verbose:
                        print(
                            'for sname = {}: {} mar, {} coh,  {} disagreed, {} did not meet ({} total)'
                            .format(sname, nmar, ncoh, ndis, nnom, ntot))

                    if np.any(i_agree_mar):

                        self.itheta[ind[i_agree_mar],
                                    t + 1] = it_out[i_agree_mar]
                        self.iexo[ind[i_agree_mar], t + 1] = iall[i_agree_mar]
                        self.state[ind[i_agree_mar], t +
                                   1] = self.state_codes['Couple and child']
                        self.iassets[ind[i_agree_mar],
                                     t + 1] = ia_out[i_agree_mar]

                        self.agreed_k[ind[i_agree_mar], t] = True
                        self.agreed_unplanned[ind[i_agree_mar],
                                              t] = i_preg[i_agree_mar] * (
                                                  sname != 'Female and child')

                        self.k_m[ind[i_agree_mar], (t + 1):] = True
                        self.k_m_true[ind[i_agree_mar],
                                      (t + 1):] = i_preg[i_agree_mar][:, None]

                        fls_policy = self.Mlist[ipol].V[
                            t + 1]['Couple and child']['fls']

                        def thti(*agrs):
                            return np.round(
                                self.tht_interpolate(*agrs)).astype(np.int8)

                        self.ils_i[ind[i_agree_mar],t+1] = \
                            thti(fls_policy,(self.iassets[ind[i_agree_mar],t+1],self.iexo[ind[i_agree_mar],t+1]),self.itheta[ind[i_agree_mar],t+1])

                        self.yaftmar[ind[i_agree_mar], t + 1] = 0
                        self.nmar[ind[i_agree_mar], t + 1:] += 1

                    if np.any(i_agree_coh):

                        assert not sname == 'Female and child'

                        self.itheta[ind[i_agree_coh],
                                    t + 1] = it_out[i_agree_coh]
                        self.iexo[ind[i_agree_coh], t + 1] = iall[i_agree_coh]
                        self.state[ind[i_agree_coh], t +
                                   1] = self.state_codes['Couple, no children']
                        self.iassets[ind[i_agree_coh],
                                     t + 1] = ia_out[i_agree_coh]

                        # FLS decision
                        #tg = self.Mlist[ipol].setup.v_thetagrid_fine
                        #fls_policy = self.V[t+1]['Couple, no children']['fls']

                        def thti(*agrs):
                            return np.round(
                                self.tht_interpolate(*agrs)).astype(np.int8)

                        fls_policy = self.Mlist[ipol].V[
                            t + 1]['Couple, no children']['fls']

                        self.ils_i[ind[i_agree_coh],t+1] = \
                            thti(fls_policy,(self.iassets[ind[i_agree_coh],t+1],self.iexo[ind[i_agree_coh],t+1]),self.itheta[ind[i_agree_coh],t+1])

                        self.yaftmar[ind[i_agree_coh], t + 1] = 0
                        self.nmar[ind[i_agree_coh], t + 1:] += 1

                    if np.any(i_disagree_or_nomeet):
                        # do not touch assets
                        fls_policy = self.Mlist[ipol].V[
                            t + 1]['Female and child']['fls']

                        if sname == 'Female and child':
                            assert not np.any(become_single)
                        if sname == 'Male, single':
                            assert not np.any(become_sm)

                        iz = izf if self.female else izm
                        self.iexo[ind[i_disagree_or_nomeet],
                                  t + 1] = iz[i_disagree_or_nomeet]
                        #self.state[ind[i_disagree_or_nomeet],t+1] = self.state_codes['Female, single']
                        self.state[ind[become_single],
                                   t + 1] = self.state_codes[ss]
                        self.state[ind[become_sm], t +
                                   1] = self.state_codes['Female and child']
                        self.ils_i[ind[become_single], t + 1] = self.ils_def
                        self.ils_i[ind[become_sm], t +
                                   1] = fls_policy[self.iassets[ind[become_sm],
                                                                t + 1],
                                                   izf[become_sm]]

                        self.yaftmar[ind[i_disagree_or_nomeet],t+1] = \
                            (self.yaftmar[ind[i_disagree_or_nomeet],t] + 1)*\
                                 (self.yaftmar[ind[i_disagree_or_nomeet],t]>=0)+\
                            (-1)*(self.yaftmar[ind[i_disagree_or_nomeet],t]<0)

                elif sname == "Couple and child" or sname == "Couple, no children":

                    ss = 'Female, single' if self.female else 'Male, single'

                    decision = self.Mlist[ipol].decisions[t][sname]

                    haschild = (sname == "Couple and child")

                    # by default keep the same theta and weights

                    self.itheta[ind, t + 1] = self.itheta[ind, t]

                    nt = self.Mlist[ipol].setup.ntheta_fine

                    # initiate renegotiation
                    isc = self.iassets[ind, t + 1]
                    iall, izf, izm, ipsi = self.Mlist[ipol].setup.all_indices(
                        t + 1, self.iexo[ind, t + 1])

                    if sname == "Couple and child":

                        transitions = self.setup.child_support_transitions[t +
                                                                           1]
                        izf_div_0 = transitions['i_this_fem'][izf, izm]
                        izf_div_1 = izf_div_0 + 1
                        wzf_div_0 = transitions['w_this_fem'][izf, izm]
                        pick_zf_0 = (self._shocks_child_support_fem[ind, t] <
                                     wzf_div_0)
                        izf_div = izf_div_0 * (pick_zf_0) + izf_div_1 * (
                            ~pick_zf_0)

                        izm_div_0 = transitions['i_this_mal'][izm]
                        izm_div_1 = izm_div_0 + 1
                        wzm_div_0 = transitions['w_this_mal'][izm]
                        pick_zm_0 = (self._shocks_child_support_mal[ind, t] <
                                     wzm_div_0)
                        izm_div = izm_div_0 * (pick_zm_0) + izm_div_1 * (
                            ~pick_zm_0)

                        if self.setup.pars['child_support_share'] < 1e-5:
                            assert np.all(izf_div == izf)
                            assert np.all(izm_div == izm)

                    elif sname == "Couple, no children":
                        izf_div = izf
                        izm_div = izm
                    else:
                        assert False

                    itht = self.itheta[ind, t + 1]
                    agrid = self.Mlist[ipol].setup.agrid_c
                    agrid_s = self.Mlist[ipol].setup.agrid_s
                    sc = agrid[isc]  # needed only for dividing asssets

                    thts_all = decision['thetas']
                    thts_orig_all = np.broadcast_to(
                        np.arange(nt)[None, None, :], thts_all.shape)

                    thts = thts_all[isc, iall, itht]
                    thts_orig = thts_orig_all[isc, iall, itht]

                    dec = decision['Decision']

                    i_stay = dec[isc,
                                 iall] if dec.ndim == 2 else dec[isc, iall,
                                                                 itht]

                    i_div = ~i_stay

                    i_ren = (i_stay) & (thts_orig != thts)
                    i_renf = (i_stay) & (thts_orig > thts)
                    i_renm = (i_stay) & (thts_orig < thts)
                    i_sq = (i_stay) & (thts_orig == thts)

                    #if self.verbose: print('{} divorce, {} ren-f, {} ren-m, {} sq'.format(np.sum(i_div),np.sum(i_renf),np.sum(i_renm),np.sum(i_sq))                     )

                    self.renegotiated[ind, t] = i_ren

                    zf_grid = self.setup.exo_grids['Female, single'][t]
                    zm_grid = self.setup.exo_grids['Male, single'][t]

                    assert np.all(self.yaftmar[ind, t] >= 0)
                    self.yaftmar[ind, t + 1] = self.yaftmar[
                        ind, t] + 1  # extra year past marriage

                    if np.any(i_div):

                        income_fem = np.exp(zf_grid[izf[i_div]])
                        income_mal = np.exp(zm_grid[izm[i_div]])

                        income_share_fem = income_fem / (income_fem +
                                                         income_mal)

                        # !!!
                        costs = self.Mlist[
                            ipol].setup.divorce_costs_k if sname == 'Couple and child' else self.Mlist[
                                ipol].setup.divorce_costs_nk

                        share_f, share_m = costs.shares_if_split(
                            income_share_fem)

                        sf = share_f * sc[i_div]
                        sm = share_m * sc[i_div]

                        self.just_divorced[ind, t] = i_div

                        if haschild:
                            self.just_divorced_k[ind, t] = i_div
                        else:
                            self.just_divorced_nk[ind, t] = i_div

                        shks = self._shocks_couple_a[ind[i_div], t]

                        # FIXME: it should be agrid_s here
                        if self.female:
                            self.iassets[ind[i_div], t + 1] = VecOnGrid(
                                agrid_s, sf).roll(shocks=shks)
                        else:
                            self.iassets[ind[i_div], t + 1] = VecOnGrid(
                                agrid, sm).roll(shocks=shks)

                        self.itheta[ind[i_div], t + 1] = -1
                        iz = izf_div if self.female else izm_div
                        self.iexo[ind[i_div], t + 1] = iz[i_div]
                        fls_policy = self.Mlist[ipol].V[
                            t + 1]['Female and child']['fls']

                        if haschild and self.female:
                            self.state[
                                ind[i_div],
                                t + 1] = self.state_codes['Female and child']
                            self.ils_i[ind[i_div], t +
                                       1] = fls_policy[self.iassets[ind[i_div],
                                                                    t + 1],
                                                       izf[i_div]]
                        else:
                            self.state[ind[i_div],
                                       t + 1] = self.state_codes[ss]
                            self.ils_i[ind[i_div], t + 1] = self.ils_def

                    if np.any(i_ren):

                        self.itheta[ind[i_ren], t + 1] = thts[i_ren]

                        #tg = self.setup.v_thetagrid_fine

                        #Distinguish between marriage and cohabitation
                        if sname == "Couple and child":
                            self.state[ind[i_ren],
                                       t + 1] = self.state_codes[sname]

                            ipick = (self.iassets[ind[i_ren], t + 1],
                                     self.iexo[ind[i_ren],
                                               t + 1], self.itheta[ind[i_ren],
                                                                   t + 1])

                            def thti(*agrs):
                                return np.round(self.tht_interpolate(
                                    *agrs)).astype(np.int8)

                            self.ils_i[ind[i_ren], t + 1] = thti(
                                self.Mlist[ipol].V[t + 1][sname]['fls'],
                                ipick[:-1], ipick[-1])
                        else:
                            i_birth = (decision['Give a birth'][isc, iall,
                                                                thts] >
                                       self._shocks_planned_preg[ind, t])
                            i_birth1 = i_birth[i_ren]

                            self.planned_preg[ind[i_ren], t] = i_birth1

                            self.m_k[ind[i_ren][i_birth1], (t + 1):] = True

                            self.new_child[ind[i_ren], t + 1] = i_birth1

                            ipick = (self.iassets[ind[i_ren], t + 1],
                                     self.iexo[ind[i_ren],
                                               t + 1], self.itheta[ind[i_ren],
                                                                   t + 1])

                            def thti(*agrs):
                                return np.round(self.tht_interpolate(
                                    *agrs)).astype(np.int8)

                            ils_if_k = thti(
                                self.Mlist[ipol].V[t + 1]["Couple and child"]
                                ['fls'], ipick[:-1], ipick[-1])
                            ils_if_nk = thti(
                                self.Mlist[ipol].V[
                                    t + 1]["Couple, no children"]['fls'],
                                ipick[:-1], ipick[-1])

                            self.ils_i[ind[i_ren],
                                       t + 1] = i_birth1 * ils_if_k + (
                                           1 - i_birth1) * ils_if_nk
                            self.state[
                                ind[i_ren],
                                t + 1] = i_birth1 * self.state_codes[
                                    "Couple and child"] + (
                                        1 - i_birth1
                                    ) * self.state_codes["Couple, no children"]

                    if np.any(i_sq):
                        self.state[ind[i_sq], t + 1] = self.state_codes[sname]

                        # do not touch theta as already updated

                        def thti(*agrs):
                            return np.round(
                                self.tht_interpolate(*agrs)).astype(np.int8)

                        if sname == "Couple and child":
                            self.state[ind[i_sq],
                                       t + 1] = self.state_codes[sname]

                            ipick = (self.iassets[ind[i_sq], t + 1],
                                     self.iexo[ind[i_sq],
                                               t + 1], self.itheta[ind[i_sq],
                                                                   t + 1])
                            self.ils_i[ind[i_sq], t + 1] = thti(
                                self.Mlist[ipol].V[t + 1][sname]['fls'],
                                ipick[:-1], ipick[-1])
                        else:
                            i_birth = (decision['Give a birth'][isc, iall,
                                                                thts] >
                                       self._shocks_planned_preg[ind, t])
                            i_birth1 = i_birth[i_sq]
                            self.m_k[ind[i_sq][i_birth1], (t + 1):] = True
                            self.planned_preg[ind[i_sq], t] = i_birth1
                            self.state[
                                ind[i_sq],
                                t + 1] = i_birth1 * self.state_codes[
                                    "Couple and child"] + (
                                        1 - i_birth1
                                    ) * self.state_codes["Couple, no children"]

                            self.new_child[ind[i_sq], t + 1] = i_birth1

                            ipick = (self.iassets[ind[i_sq], t + 1],
                                     self.iexo[ind[i_sq],
                                               t + 1], self.itheta[ind[i_sq],
                                                                   t + 1])

                            ils_if_k = thti(
                                self.Mlist[ipol].V[t + 1]["Couple and child"]
                                ['fls'], ipick[:-1], ipick[-1])
                            ils_if_nk = thti(
                                self.Mlist[ipol].V[
                                    t + 1]["Couple, no children"]['fls'],
                                ipick[:-1], ipick[-1])

                            self.ils_i[ind[i_sq],
                                       t + 1] = i_birth1 * ils_if_k + (
                                           1 - i_birth1) * ils_if_nk
                            self.state[
                                ind[i_sq],
                                t + 1] = i_birth1 * self.state_codes[
                                    "Couple and child"] + (
                                        1 - i_birth1
                                    ) * self.state_codes["Couple, no children"]

                else:
                    raise Exception('unsupported state?')

        assert not np.any(np.isnan(self.state[:, t + 1]))
Example #13
0
def v_mar_igrid(setup,
                t,
                V,
                icouple,
                ind_or_inds,
                *,
                female,
                marriage,
                interpolate=True,
                return_all=False):
    # this returns value functions for couple that entered the last period with
    # (s,Z,theta) from the grid and is allowed to renegotiate them or breakup

    # if return_all==False returns Vout_f, Vout_m, that are value functions
    # of male and female from entering this union
    # if return_all==True returns (Vout_f, Vout_m, ismar, thetaout, technical)
    # where ismar is marriage decision, thetaout is resulting theta and
    # tuple technical contains less usable stuff (check v_newmar_core for it)
    #
    # combine = True creates matrix (n_s-by-n_inds)
    # combine = False assumed that n_s is the same shape as n_inds and creates
    # a flat array.

    if marriage:
        coup = 'Couple, M'
    else:
        coup = 'Couple, C'

    dtype = setup.dtype
    # import objects
    agrid_c = setup.agrid_c
    agrid_s = setup.agrid_s
    gamma = setup.pars['m_bargaining_weight']

    VMval_single, VFval_single = V['Male, single']['V'], V['Female, single'][
        'V']
    VMval_postren, VFval_postren = V[coup]['VM'][icouple,
                                                 ...], V[coup]['VF'][icouple,
                                                                     ...]

    # substantial part
    ind, izf, izm, ipsi = setup.all_indices(t, ind_or_inds)

    # using trim = True implicitly trims things on top
    # so if sf is 0.75*amax and sm is 0.75*amax then sc is 1*amax and not 1.5

    #sc = sf+sm # savings of couple
    s_partner = agrid_c[icouple] - agrid_s  # we assume all points on grid

    # this implicitly trims negative or too large values
    s_partner_v = VecOnGrid(agrid_s, s_partner,
                            trim=True) if len(agrid_s) > 1 else s_partner

    # this applies them

    if female:
        Vfs = VFval_single[:, izf]
        Vms = s_partner_v.apply(VMval_single, axis=0, take=(
            1, izm)) if len(agrid_s) > 1 else VMval_single[:, izm]
    else:
        Vms = VMval_single[:, izm]
        Vfs = s_partner_v.apply(VFval_single, axis=0, take=(
            1, izf)) if len(agrid_s) > 1 else VFval_single[:, izf]

    expnd = lambda x: setup.v_thetagrid_fine.apply(x, axis=2)

    Vmm, Vfm = (expnd(x[:, ind, :]) for x in (VMval_postren, VFval_postren))

    assert Vmm.dtype == dtype

    ins = [Vfm, Vmm, Vfs, Vms, np.array(gamma)]
    ins = [x.astype(dtype, copy=False)
           for x in ins]  # optional type conversion
    vfout, vmout, nbsout, agree, ithetaout = mar_mat(*ins)

    return {
        'Values': (vfout, vmout),
        'NBS': nbsout,
        'theta': ithetaout,
        'Decision': agree
    }
Example #14
0
def mar_graphs(mdl,t=1):
    setup = mdl.setup
    V = mdl.V
    from marriage import v_mar_igrid
    from gridvec import VecOnGrid
    agrid_c = mdl.setup.agrid_c
    agrid_s = mdl.setup.agrid_s
    icouple = VecOnGrid(agrid_c,2*agrid_s).i
    
    npsi = setup.pars['n_psi_t'][t]
    psig = setup.exogrid.psi_t[t]
    nzf = setup.pars['n_zf_t'][t]
    zfg = setup.exogrid.zf_t[t]
    zmg = setup.exogrid.zm_t[t]
    
    
    
    izm = 2
    wm = setup.pars['m_wage_trend'][t] + zmg[izm]
    wzf = np.exp( setup.pars['f_wage_trend'][t] + zfg) / np.exp(wm)
    
    '''
    
    print(psig)
    izf = 3
    izm = 2
    inds_ = (izf*np.ones(npsi,dtype=np.int16),izm*np.ones(npsi,dtype=np.int16),np.arange(npsi,dtype=np.int16))
    inds = mdl.setup.all_indices(t,inds_)[0]
    
    '''
    inds = mdl.setup.all_indices(t)[0]
    iac = np.searchsorted(agrid_c,2.0*wm)
    ias = np.searchsorted(agrid_s,1.0*wm)
    results = list()
    for upp in [False,True]:
        uloss = 0.0 #setup.pars['disutil_shotgun'] if upp else 0.0
        res = v_mar_igrid(setup,t,V[t],icouple,inds,female=True,giveabirth=upp,
                                          unplanned_pregnancy=upp,
                                          uloss_fem=0.0,uloss_mal=0.0,
                                          uloss_fem_single=uloss,uloss_mal_single=uloss,
                                          return_all=True)
    
        res_r = mdl.x_reshape(res['theta'][...,None],t).squeeze(axis=-1)
        tht_r = setup.thetagrid_fine[res_r]
        #tht_v[res['theta'] < 0] = None
        tht_all = ma.masked_where(res_r<0,tht_r)
        tht_pick = tht_all[iac,:,izm,:]
        print(tht_pick.shape)
        fig, ax = plt.subplots()
        cs = ax.contourf(zfg,psig,tht_pick.T,cmap='Blues',vmin=0.0,vmax=0.8) 
        cb = fig.colorbar(cs)
        cb.set_label(r'Resulting female bargaining power ($\theta$)')
        plt.xlabel('Female productivity')
        plt.ylabel(r'Love shock at match ($\psi$)',labelpad=-3.0)
        plt.title('Bargaining: {}'.format('Unplanned Pregnancy (No Stigma)' if upp else 'Regular Match')) 
        results.append(res)
        #ax.grid(True)
        ax.set_xticks(zfg)
        ax.set_yticks(psig)#np.arange(-4,5))
        plt.savefig(('barg_upp.pdf' if upp else 'barg_reg_match.pdf'))
        
        
    result_noupp, result_upp = results
    
    
    izf = 3
    izm = 3
    ipsi = 9
    
    fig, axs = plt.subplots(1,2)
    fig2, axs2 = plt.subplots()
    
    
    
    
    
    for n, upp, res in zip([0,1],[False,True],[result_noupp,result_upp]):
        Vfm,Vmm,Vfs,Vms,gamma = res['ins']
        Vfm0, Vmm0 = [mdl.x_reshape(x,t) for x in [Vfm,Vmm]]
        Vfs0,Vms0 = [mdl.x_reshape(x[...,None],t) for x in [Vfs,Vms]]
        
        
        
        if not upp: 
            norm_f = Vfs0[iac,izf,izm,ipsi]
            norm_m = Vms0[iac,izf,izm,ipsi]
            
        vfm_tht = Vfm0[iac,izf,izm,ipsi,:] - norm_f
        vmm_tht = Vmm0[iac,izf,izm,ipsi,:] - norm_m
        
        
        
        
        vfs_tht = Vfs0[iac,izf,izm,ipsi]*np.ones(vfm_tht.size) - norm_f
        vms_tht = Vms0[iac,izf,izm,ipsi]*np.ones(vmm_tht.size) - norm_m
        
        
        
        tht_fine = setup.thetagrid_fine
        
        l = 'unplanned pregnancy' if upp else 'regular match'
        c = 'o' if upp else 'x'

        axs[0].plot(tht_fine,vfm_tht,'{}-k'.format(c),label='Agree, {}'.format(l),markevery=25)
        axs[0].plot(tht_fine,vfs_tht,'{}--k'.format(c),label='Disagree, {}'.format(l),markevery=25)
        axs[1].plot(tht_fine,vmm_tht,'{}-k'.format(c),label='Agree, {}'.format(l),markevery=25)
        axs[1].plot(tht_fine,vms_tht,'{}--k'.format(c),label='Disagree, {}'.format(l),markevery=25)
        axs[1].set_title('Male')
        axs[0].set_title('Female')
        
        axs2.plot(tht_fine,vfm_tht-vfs_tht,'{}-k'.format(c),label='F, {}'.format(l),markevery=25)
        axs2.plot(tht_fine,vmm_tht-vms_tht,'{}--k'.format(c),label='M, {}'.format(l),markevery=25)
        if not upp: axs2.plot(tht_fine,np.zeros_like(tht_fine),':k'.format(c),markevery=25)
        
    axs[0].set_ylabel(r'Value functions (normalized)')
    [ax.set_xlabel(r'Bargaining power ($\theta$)') for ax in axs]
    axs[1].legend(bbox_to_anchor=(-1.4, -0.175),loc='upper left',ncol=2)
    fig.suptitle('Change in values b/c of unplanned pregnancy',fontsize=16)
    fig.subplots_adjust(bottom=+0.25)
    fig.savefig('change_upp.pdf',bbox='tight',pad_inches=0.5)
    fig2.suptitle('Surplus over disagreement',fontsize=16)
    axs2.legend(ncol=1)
    axs2.set_xlim(0.0,1.0)
    #axs[0].set_ylim(-50,10)
    #axs[1].set_ylim(-50,10)
    
        
    

        
    
    
    
    
        
        
Example #15
0
                def couples():

                    ss = self.single_state

                    decision = self.Mlist[ipol].decisions[t][sname]

                    # by default keep the same theta and weights

                    self.itheta[ind, t + 1] = self.itheta[ind, t]

                    nt = self.Mlist[ipol].setup.ntheta_fine

                    # initiate renegotiation
                    isc = self.iassets[ind, t + 1]
                    iall, izf, izm, ipsi = self.Mlist[ipol].setup.all_indices(
                        t + 1, self.iexo[ind, t + 1])

                    iz = izf if self.female else izm

                    itht = self.itheta[ind, t + 1]
                    agrid = self.Mlist[ipol].setup.agrid_c
                    agrids = self.Mlist[ipol].setup.agrid_s
                    sc = agrid[isc]  # needed only for dividing asssets

                    thts_all = decision['thetas']
                    thts_orig_all = np.broadcast_to(
                        np.arange(nt)[None, None, :], thts_all.shape)

                    thts = thts_all[isc, iall, itht]
                    thts_orig = thts_orig_all[
                        isc, iall,
                        itht]  #this line below takes 43% of the time in coupls

                    dec = decision['Decision']
                    #this guy below account for 24% of the time in couple
                    i_stay = dec[isc, iall] if dec.ndim == 2 else dec[
                        isc, iall, itht]  #i_stay = dec[isc,iall,itht]

                    bil_bribing = ('Bribing' in decision)

                    i_div = ~i_stay

                    #ifem=decision['Divorce'][0][isc,iall][...,None]<self.Mlist[ipol].V[t]['Couple, M']['VF'][isc,iall,:]
                    #imal=decision['Divorce'][1][isc,iall][...,None]<self.Mlist[ipol].V[t]['Couple, M']['VM'][isc,iall,:]
                    #both=~np.max((ifem) & (imal),axis=1)

                    i_ren = (i_stay) & (thts_orig != thts)
                    i_renf = (i_stay) & (thts_orig > thts)
                    i_renm = (i_stay) & (thts_orig < thts)
                    i_sq = (i_stay) & (thts_orig == thts)

                    if self.verbose:
                        print('{} divorce, {} ren-f, {} ren-m, {} sq'.format(
                            np.sum(i_div), np.sum(i_renf), np.sum(i_renm),
                            np.sum(i_sq)))

                    zf_grid = self.setup.exo_grids['Female, single'][t]
                    zm_grid = self.setup.exo_grids['Male, single'][t]

                    if np.any(i_div):

                        income_fem = np.exp(zf_grid[izf[i_div]] +
                                            self.setup.pars['f_wage_trend'][t])
                        income_mal = np.exp(zm_grid[izm[i_div]] +
                                            self.setup.pars['m_wage_trend'][t])

                        income_share_fem = (income_fem) / (income_fem +
                                                           income_mal)

                        # this part determines assets
                        costs = self.Mlist[
                            ipol].setup.div_costs if sname == 'Couple, M' else self.Mlist[
                                ipol].setup.sep_costs

                        share_f, share_m = costs.shares_if_split(
                            income_share_fem)

                        #Uncomment bnelowe if ren_theta
                        share_f = costs.shares_if_split_theta(
                            self.setup, self.setup.thetagrid[
                                self.setup.v_thetagrid_fine.i[itht] +
                                1])[i_div]
                        share_m = 1 - share_f

                        #sf = share_f[i_div]*sc[i_div]
                        #assert np.all(share_f[i_div]>=0) and np.all(share_f[i_div]<=1)
                        #sm = share_m[i_div]*sc[i_div]

                        sf = share_f * sc[i_div]
                        assert np.all(share_f >= 0) and np.all(share_f <= 1)
                        sm = share_m * sc[i_div]

                        s = sf if self.female else sm
                        shks = 1 - self.shocks_div_a[ind[i_div], t]

                        # if bribing happens we overwrite this
                        self.iassets[ind[i_div], t + 1] = VecOnGrid(
                            self.Mlist[ipol].setup.agrid_s,
                            s).roll(shocks=shks)

                        if bil_bribing:

                            iassets = decision['Bribing'][
                                1] if self.female else decision['Bribing'][2]
                            do_bribing = decision['Bribing'][0]

                            iassets_ifdiv = iassets[
                                isc[i_div], iall[i_div],
                                itht[i_div]]  # assets resulted from bribing
                            do_b = do_bribing[
                                isc[i_div], iall[i_div],
                                itht[i_div]]  # True / False if bribing happens
                            assert np.all(iassets_ifdiv[do_b] >= 0)

                            if np.any(do_b):
                                #n_b = np.sum(do_b)
                                #n_tot = np.sum(i_div)
                                #share_b = int(100*n_b/n_tot)
                                #print('bribing happens in {} cases, that is {}% of all divorces'.format(n_b,share_b))
                                self.iassets[ind[i_div][do_b],
                                             t + 1] = iassets_ifdiv[do_b]

                                #print(np.mean(agrid[isc[i_div][do_b]]/(agrids[decision['Bribing'][1][isc[i_div][do_b],iall[i_div][do_b],itht[i_div][do_b]]]+
                                #                                      agrids[decision['Bribing'][2][isc[i_div][do_b],iall[i_div][do_b],itht[i_div][do_b]]])))

                                #aaa=self.Mlist[ipol].setup.agrid_c[self.iassets[ind[i_div][do_b],t+1]]/(self.Mlist[ipol].setup.agrid_c[self.iassetss[ind[i_div][do_b],t+1]])
                                #aaa1=(self.Mlist[ipol].setup.agrid_c[self.iassetss[ind[i_div][do_b],t+1]]>0)
                                #if sname == "Couple, M":print(np.mean(aaa[aaa1]))

                        self.itheta[ind[i_div], t + 1] = -1
                        self.iexo[ind[i_div], t + 1] = iz[i_div]
                        self.state[ind[i_div], t + 1] = self.state_codes[ss]
                        if sname == "Couple, M":
                            self.divorces[ind[i_div], t + 1] = True

                        #FLS
                        self.ils_i[ind[i_div], t + 1] = self.ils_def

                    if np.any(i_ren):

                        self.itheta[ind[i_ren], t + 1] = thts[i_ren]

                        #tg = self.setup.v_thetagrid_fine

                        #Distinguish between marriage and cohabitation
                        if sname == "Couple, M":
                            self.state[ind[i_ren],
                                       t + 1] = self.state_codes[sname]

                            ipick = (self.iassets[ind[i_ren], t + 1],
                                     self.iexo[ind[i_ren],
                                               t + 1], self.itheta[ind[i_ren],
                                                                   t + 1])
                            self.ils_i[ind[i_ren],
                                       t + 1] = self.Mlist[ipol].decisions[
                                           t + 1][sname]['fls'][ipick]
                        else:
                            i_coh = decision[
                                'Cohabitation preferred to Marriage'][isc,
                                                                      iall,
                                                                      thts]
                            i_coh1 = i_coh[i_ren]

                            ipick = (self.iassets[ind[i_ren], t + 1],
                                     self.iexo[ind[i_ren],
                                               t + 1], self.itheta[ind[i_ren],
                                                                   t + 1])
                            ils_if_mar = self.Mlist[ipol].decisions[
                                t + 1]["Couple, M"]['fls'][ipick]
                            ils_if_coh = self.Mlist[ipol].decisions[
                                t + 1]["Couple, C"]['fls'][ipick]

                            self.ils_i[ind[i_ren],
                                       t + 1] = i_coh1 * ils_if_coh + (
                                           1 - i_coh1) * ils_if_mar
                            self.state[
                                ind[i_ren], t +
                                1] = i_coh1 * self.state_codes["Couple, C"] + (
                                    1 - i_coh1) * self.state_codes["Couple, M"]

                    if np.any(i_sq):
                        self.state[ind[i_sq], t + 1] = self.state_codes[sname]
                        # do not touch theta as already updated

                        #Distinguish between marriage and cohabitation
                        if sname == "Couple, M":
                            self.state[ind[i_sq],
                                       t + 1] = self.state_codes[sname]

                            ipick = (self.iassets[ind[i_sq], t + 1],
                                     self.iexo[ind[i_sq],
                                               t + 1], self.itheta[ind[i_sq],
                                                                   t + 1])
                            self.ils_i[ind[i_sq],
                                       t + 1] = self.Mlist[ipol].decisions[
                                           t + 1][sname]['fls'][ipick]
                        else:
                            i_coh = decision[
                                'Cohabitation preferred to Marriage'][isc,
                                                                      iall,
                                                                      thts]
                            i_coh1 = i_coh[i_sq]
                            self.state[
                                ind[i_sq], t +
                                1] = i_coh1 * self.state_codes["Couple, C"] + (
                                    1 - i_coh1) * self.state_codes["Couple, M"]

                            ipick = (self.iassets[ind[i_sq], t + 1],
                                     self.iexo[ind[i_sq],
                                               t + 1], self.itheta[ind[i_sq],
                                                                   t + 1])

                            ils_if_mar = self.Mlist[ipol].decisions[
                                t + 1]["Couple, M"]['fls'][ipick]
                            ils_if_coh = self.Mlist[ipol].decisions[
                                t + 1]["Couple, C"]['fls'][ipick]

                            self.ils_i[ind[i_sq],
                                       t + 1] = i_coh1 * ils_if_coh + (
                                           1 - i_coh1) * ils_if_mar
                            self.state[
                                ind[i_sq], t +
                                1] = i_coh1 * self.state_codes["Couple, C"] + (
                                    1 - i_coh1) * self.state_codes["Couple, M"]
Example #16
0
    def anext(self, t):
        # finds savings (potenitally off-grid)

        for ipol in range(self.npol):
            for ist, sname in enumerate(self.state_codes):

                is_state_any_pol = (self.state[:, t] == ist)
                is_pol = (self.policy_ind[:, min(t + 1, self.T - 1)] == ipol)

                is_state = (is_state_any_pol) & (is_pol)

                use_theta = self.has_theta[ist]
                nst = np.sum(is_state)

                if nst == 0:
                    continue

                ind = np.where(is_state)[0]

                pol = self.Mlist[ipol].decisions[t][sname]

                if not use_theta:

                    #Dictionaries below account for 90% of the time in this function

                    anext = pol['s'][self.iassets[ind, t], self.iexo[ind, t]]
                    if t + 1 < self.T:
                        self.iassets[ind, t + 1] = VecOnGrid(
                            self.setup.agrid_s,
                            anext).roll(shocks=self.shocks_single_a[ind, t])
                        self.iassetss[ind, t + 1] = self.iassets[ind,
                                                                 t + 1].copy()
                    self.s[ind, t] = anext
                    if self.draw:
                        self.c[ind, t] = pol['c'][self.iassets[ind, t],
                                                  self.iexo[ind, t]]
                    if self.draw:
                        self.x[ind, t] = pol['x'][self.iassets[ind, t],
                                                  self.iexo[ind, t]]

                else:

                    # interpolate in both assets and theta
                    # function apply_2dim is experimental but I checked it at this setup

                    # apply for couples

                    anext = pol['s'][self.iassets[ind, t], self.iexo[ind, t],
                                     self.itheta[ind, t]]
                    self.s[ind, t] = anext
                    if self.draw:
                        self.x[ind, t] = pol['x'][self.iassets[ind, t],
                                                  self.iexo[ind, t],
                                                  self.itheta[ind, t]]
                    if self.draw:
                        self.c[ind, t] = pol['c'][self.iassets[ind, t],
                                                  self.iexo[ind, t],
                                                  self.itheta[ind, t]]
                    if t + 1 < self.T:
                        self.iassets[ind, t + 1] = VecOnGrid(
                            self.setup.agrid_c,
                            anext).roll(shocks=self.shocks_couple_a[ind, t])
                        self.iassetss[ind, t + 1] = self.iassets[ind,
                                                                 t + 1].copy()

                assert np.all(anext >= 0)
Example #17
0
    def __init__(self,
                 Mlist,
                 age_uni,
                 female=False,
                 pswitchlist=None,
                 N=15000,
                 T=None,
                 verbose=True,
                 nosim=False,
                 draw=False):

        np.random.seed(8)

        # take the stuff from the model and arguments
        # note that this does not induce any copying just creates links

        if type(Mlist) is not list:
            Mlist = [Mlist]

        #Unilateral Divorce
        self.Mlist = Mlist
        self.Vlist = [M.V for M in Mlist]
        self.declist = [M.decisions for M in Mlist]
        self.npol = len(Mlist)
        self.transition = len(self.Mlist) > 1

        if T is None:
            T = self.Mlist[0].setup.pars['T']

        self.setup = self.Mlist[0].setup
        self.state_names = self.setup.state_names
        self.N = N
        self.T = T
        self.verbose = verbose
        self.timer = self.Mlist[0].time
        self.draw = draw

        self.female = female
        self.single_state = 'Female, single' if female else 'Male, single'

        #Divorces
        self.divorces = np.zeros((N, T), bool)

        # all the randomness is here
        shokko = np.random.random_sample((9, N, T))
        self.shocks_single_iexo2 = shokko[
            8, :, :]  # np.random.random_sample((N,T))
        self.shocks_single_iexo = shokko[
            0, :, :]  # np.random.random_sample((N,T))
        self.shocks_single_meet = shokko[
            1, :, :]  # np.random.random_sample((N,T))
        self.shocks_couple_iexo = shokko[
            2, :, :]  # np.random.random_sample((N,T))
        self.shocks_single_a = shokko[
            3, :, :]  # np.random.random_sample((N,T))
        self.shocks_couple_a = shokko[
            4, :, :]  # np.random.random_sample((N,T))

        self.shocks_div_a = shokko[5, :, :]  #np.random.random_sample((N,T))

        z_t = self.setup.exogrid.zf_t if female else self.setup.exogrid.zm_t
        sig = self.setup.pars['sig_zf_0'] if female else self.setup.pars[
            'sig_zm_0']
        z_prob = int_prob(z_t[0], sig=sig)
        shocks_init = shokko[6, :, 0]  #np.random.random_sample((N,))
        i_z = np.sum((shocks_init[:, None] > np.cumsum(z_prob)[None, :]),
                     axis=1)
        iexoinit = i_z  # initial state

        self.shocks_transition = shokko[
            7, :, :]  #np.random.random_sample((N,T))
        # no randomnes past this line please

        # initialize assets

        self.iassets = np.zeros((N, T), np.int16)
        self.iassetss = np.zeros((N, T), np.int16)
        self.tempo = VecOnGrid(self.setup.agrid_s, self.iassets[:, 0])

        # initialize FLS
        #self.ils=np.ones((N,T),np.float64)
        self.ils_i = np.ones((N, T), np.int8) * (len(self.setup.ls_levels) - 1)

        self.ils_i[:, -1] = 5

        # initialize theta
        self.itheta = -np.ones((N, T), np.int16)

        # initialize iexo
        self.iexo = np.zeros((N, T), np.int16)
        self.iexos = np.zeros((N, T), np.int16)
        # TODO: look if we can/need fix the shocks here...

        self.iexo[:, 0] = iexoinit
        self.iexos[:, 0] = iexoinit

        # NB: the last column of these things will not be filled
        # c refers to consumption expenditures (real consumption of couples
        # may be higher b/c of returns to scale)
        self.c = np.zeros((N, T), np.float32)
        self.x = np.zeros((N, T), np.float32)
        self.s = np.zeros((N, T), np.float32)

        self.state_codes = dict()
        self.has_theta = list()
        for i, name in enumerate(self.setup.state_names):
            self.state_codes[name] = i
            self.has_theta.append((name == 'Couple, C' or name == 'Couple, M'))

        # initialize state
        self.state = np.zeros((N, T), dtype=np.int8)
        self.state[:, 0] = self.state_codes[
            self.single_state]  # everyone starts as female

        self.timer('Simulations, creation', verbose=self.verbose)
        self.ils_def = self.setup.nls - 1

        #Create a file with the age of the change foreach person

        self.policy_ind = np.zeros((N, T), dtype=np.int8)

        if pswitchlist == None:
            pswitchlist = [np.eye(self.npol)] * T

        # this simulates "exogenous" transitions of polciy functions
        # policy_ind stands for index of the policies to apply, they are
        # from 0 to (self.npol-1)
        zeros = np.zeros((N, ), dtype=np.int8)
        mat_init = pswitchlist[0]

        self.policy_ind[:, 0] = mc_simulate(
            zeros, mat_init,
            shocks=self.shocks_transition[:, 0])  # everyone starts with 0
        if self.npol > 1:
            for t in range(T - 1):
                mat = pswitchlist[t + 1]
                self.policy_ind[:, t + 1] = mc_simulate(
                    self.policy_ind[:, t],
                    mat,
                    shocks=self.shocks_transition[:, t + 1])
        else:
            self.policy_ind[:] = 0

        if not nosim: self.simulate()
Example #18
0
def v_mar(setup, V, t, iassets_couple, iexo_couple, *, match_type, female):

    # this builds matrix for all matches specified by grid positions
    # iassets_couple (na X nmatches) and iexo_couple (nmatches)

    iexo, izf, izm, ipsi = setup.all_indices(t, iexo_couple)

    vals = pick_values(setup, V, match_type=match_type)
    V_fem, V_mal = vals['V_fem'], vals['V_mal']

    # obtain partner's value
    assets_partner = np.clip(
        setup.agrid_c[iassets_couple] - setup.agrid_s[:, None], 0.0,
        setup.agrid_s.max())
    # this can be fastened by going over repeated values of ipsi
    v_assets_partner = VecOnGrid(setup.agrid_s, assets_partner)
    i, wnext, wthis = v_assets_partner.i, v_assets_partner.wnext, v_assets_partner.wthis

    if female:
        V_f_no = V_fem[:, izf]
        V_m_no = V_mal[i, izm[None, :]] * wthis + V_mal[i + 1,
                                                        izm[None, :]] * wnext
    else:
        V_f_no = V_fem[i, izf[None, :]] * wthis + V_fem[i + 1,
                                                        izf[None, :]] * wnext
        V_m_no = V_mal[:, izm]

    V_fem_mar, V_mal_mar = vals['V_fem_mar'], vals['V_mal_mar']

    ia = iassets_couple

    V_f_yes = V_fem_mar[ia, iexo[None, :], :]
    V_m_yes = V_mal_mar[ia, iexo[None, :], :]

    assert V_f_yes.shape[:-1] == V_f_no.shape
    assert V_m_yes.shape[:-1] == V_m_no.shape

    # fill abortion decisions
    if match_type == 'Unplanned pregnancy' and female:
        do_abortion = vals['i_abortion'][:, izf]
    else:
        do_abortion = np.zeros(V_f_no.shape, dtype=np.bool_)

    it, wnt = setup.v_thetagrid_fine.i, setup.v_thetagrid_fine.wnext

    from marriage_gpu import v_mar_gpu

    if not ugpu:
        v_f, v_m, agree, nbs, itheta = get_marriage_values(
            V_f_yes, V_m_yes, V_f_no, V_m_no, it, wnt)
    else:
        v_f, v_m, agree, nbs, itheta = v_mar_gpu(V_f_yes, V_m_yes, V_f_no,
                                                 V_m_no, it, wnt)

    return {
        'V_fem': v_f,
        'V_mal': v_m,
        'Agree': agree,
        'NBS': nbs,
        'itheta': itheta,
        'Abortion': do_abortion
    }
Example #19
0
    def __init__(self,
                 nogrid=False,
                 divorce_costs_k='Default',
                 divorce_costs_nk='Default',
                 **kwargs):
        p = dict()
        T = 55
        Tret = 45  # first period when the agent is retired
        Tfert = 18  # first peroid when infertile
        Tdiv = 44  # first period when cannot divorce / renegotiate
        Tmeet = 25  # first period when stop meeting partners
        Tinc = 25  # first period where stop tracking income process and assume it to be fixed
        p['T'] = T
        p['Tret'] = Tret
        p['Tfert'] = Tfert
        p['Tsim'] = T
        p['n_zf_t'] = [7] * Tret + [1] * (T - Tret)
        p['n_zm_t'] = [5] * Tret + [1] * (T - Tret)
        p['sigma_psi_mult'] = 0.28
        p['sigma_psi'] = 0.11
        p['R_t'] = [1.025] * T
        p['n_psi'] = 15
        p['beta_t'] = [0.98] * T
        p['A'] = 1.0  # consumption in couple: c = (1/A)*[c_f^(1+rho) + c_m^(1+rho)]^(1/(1+rho))
        p['crra_power'] = 1.5
        p['couple_rts'] = 0.23
        p['sig_partner_a'] = 0.1
        p['mu_partner_a_female'] = 0.00
        p['mu_partner_a_male'] = -0.00
        p['dump_factor_z'] = 0.75
        p['sig_partner_z'] = 0.25
        p['mu_partner_z_male'] = -0.02
        p['mu_partner_z_female'] = 0.02
        p['m_bargaining_weight'] = 0.5

        p['pmeet_21'] = 0.1
        p['pmeet_28'] = 0.2
        p['pmeet_35'] = 0.1

        p['m_zf'] = 0.9
        p['m_zf0'] = 1.0

        p['z_drift'] = -0.09
        p['no kids at meeting'] = True
        p['high education'] = True  # what trend to pick
        p['any kids'] = True

        p['wret'] = 0.8
        p['uls'] = 0.2
        p['pls'] = 1.0

        p['income_sd_mult'] = 1.0
        p['pay_gap'] = True

        p['preg_mult'] = 1.0

        p['u_shift_mar'] = 0.0
        p['u_shift_coh'] = 0.0
        p['sm_shift'] = 0.0

        p['disutil_marry_sm_fem_coef'] = 0.0
        p['disutil_marry_sm_mal_coef'] = 10.0
        p['disutil_shotgun_coef'] = 2.0
        p['pmeet_multiplier_fem'] = 1.0
        p['p_to_meet_sm_if_mal'] = 0.1

        p['taste_shock_mult'] = 1.0

        p['p_abortion_access'] = 0.5
        p['abortion_costs_mult'] = 10.0

        p['u_lost_divorce_mult'] = 0.0

        p['child_a_cost'] = 0.0
        p['child_support_share'] = 0.0

        p['util_lam'] = 0.7
        p['util_alp'] = 0.5
        p['util_xi'] = 1.5
        p['util_kap'] = 0.5
        p['util_qbar'] = 0.0

        p['util_out_lf'] = 0.0

        p['preg_21'] = 0.01
        p['preg_28'] = 0.5
        p['preg_35'] = 0.3

        for key, value in kwargs.items():
            assert (key in p), 'wrong name?'
            p[key] = value

        if p['high education']:
            p['sig_zm'] = p['income_sd_mult'] * 0.16138593
            p['sig_zm_0'] = p['income_sd_mult'] * 0.41966813

            # FIXME: I need guidance how to pin these down
            p['sig_zf'] = p['income_sd_mult'] * p['m_zf'] * 0.19571624
            p['sig_zf_0'] = p['income_sd_mult'] * p['m_zf0'] * 0.43351219
        else:
            p['sig_zm'] = p['income_sd_mult'] * 0.2033373
            p['sig_zm_0'] = p['income_sd_mult'] * 0.40317171

            p['sig_zf'] = p['income_sd_mult'] * p['m_zf'] * 0.14586778
            p['sig_zf_0'] = p['income_sd_mult'] * p['m_zf0'] * 0.62761052

        # college

        if p['high education']:

            m_trend_data = [
                0.0, 0.11316599, .2496034, .31260625, .37472204, .4268548,
                .48067884, .52687573, .57293878, .60941412, .65015743,
                .6685226, .72482815, .74446455, .76712521, .78038137,
                .79952806, .80092523, .81972567, .82913486, .83849471,
                .84308452, .84646086, .85437072, .85499576
            ]

            f_trend_data = [
                0.0, 0.06715984, .21149606, .32283002, .46885336, .52712037,
                .58302632, .63348555, .68024646, .71450132, .74246337,
                .77044807, .79946406, .80640353, .83799304, .85356081,
                .86832235, .87407447, .87820755, .86840901, .87630054,
                .8765972, .87894493, .87800553, .87932908
            ]

            nm = len(m_trend_data) - 1
            nf = len(f_trend_data) - 1

            t0 = 4
            gap = 3.0340077 - 2.8180354  # male - female

            c_female = -f_trend_data[t0]
            c_male = gap - m_trend_data[t0]

            p['m_wage_trend'] = np.array(
                [c_male + m_trend_data[min(t, nm)] for t in range(T)])
            p['f_wage_trend'] = np.array(
                [c_female + f_trend_data[min(t, nf)] for t in range(T)])

        else:
            # no college

            p['m_wage_trend'] = np.array([
                -0.2424105 + 0.037659 * (min(t + 2, 30) - 5) - 0.0015337 *
                ((min(t + 2, 30) - 5)**2) + 0.000026 *
                ((min(t + 2, 30) - 5)**3) for t in range(T)
            ])
            p['f_wage_trend'] = np.array([
                -0.3668214 + 0.0264887 * (min(t, 30) - 5) - 0.0012464 *
                ((min(t, 30) - 5)**2) + 0.0000251 * ((min(t, 30) - 5)**3)
                for t in range(T)
            ])

        if not p['pay_gap']:
            p['sig_zf'], p['sig_zf_0'] = p['sig_zm'], p['sig_zm_0']
            p['f_wage_trend'] = p['m_wage_trend']

        # derivative parameters
        p['sigma_psi_init'] = p['sigma_psi_mult'] * p['sigma_psi']

        p['disutil_marry_sm_mal'] = p['disutil_marry_sm_mal_coef'] * p[
            'u_shift_mar']
        p['disutil_marry_sm_fem'] = p['disutil_marry_sm_fem_coef'] * p[
            'u_shift_mar']
        p['disutil_shotgun'] = p['disutil_shotgun_coef'] * p['sigma_psi_init']
        p['abortion_costs'] = p['abortion_costs_mult'] * p['u_shift_mar']
        p['u_lost_divorce'] = p['u_lost_divorce_mult'] * p['sigma_psi_init']

        p['preg_az'] = 0.00
        p['preg_azt'] = 0.00

        #Get the probability of meeting, adjusting for year-period

        p['taste_shock'] = 0.0 * p['taste_shock_mult'] * 0.0  #p['sigma_psi']

        p['is fertile'] = [p['any kids']] * Tfert + [False] * (T - Tfert)
        p['can divorce'] = [True] * Tdiv + [False] * (T - Tdiv)
        #p['poutsm_t'] = [p['poutsm']]*T

        p['pmeet_0'], p['pmeet_t'], p['pmeet_t2'] = prob_polyfit(
            (p['pmeet_21'], 0), (p['pmeet_28'], 7), (p['pmeet_35'], 14),
            max_power=2)

        p['preg_a0'], p['preg_at'], p['preg_at2'] = prob_polyfit(
            (p['preg_21'], 0), (p['preg_28'], 7), (p['preg_35'], 14),
            max_power=2)

        p['pmeet_t'] = [
            np.clip(p['pmeet_0'] + t * p['pmeet_t'] +
                    (t**2) * p['pmeet_t2'], 0.0, 1.0) for t in range(Tmeet)
        ] + [0.0] * (T - Tmeet)

        p['n_psi_t'] = [p['n_psi']] * T

        self.pars = p

        self.dtype = np.float64  # type for all floats

        # relevant for integration
        self.state_names = [
            'Female, single', 'Male, single', 'Female and child',
            'Couple, no children', 'Couple and child'
        ]

        # female labor supply

        lmin = 0.2
        lmax = 1.0
        nl = 2

        ls = np.array([0.2, 1.0])  #np.linspace(lmin,lmax,nl,dtype=self.dtype)
        ps = np.array([p['pls'], 0.0])
        ls_ushift = np.array([p['util_out_lf'], 0.0])

        self.ls_levels = dict()
        self.ls_levels['Couple, no children'] = np.array([1.0],
                                                         dtype=self.dtype)
        self.ls_levels['Female, single'] = np.array([1.0], dtype=self.dtype)
        self.ls_levels['Male, single'] = np.array([1.0], dtype=self.dtype)
        self.ls_levels['Couple and child'] = ls
        self.ls_levels['Female and child'] = ls

        self.ls_ushift = dict()
        self.ls_ushift['Couple, no children'] = np.array([0.0],
                                                         dtype=self.dtype)
        self.ls_ushift['Female, single'] = np.array([0.0], dtype=self.dtype)
        self.ls_ushift['Male, single'] = np.array([0.0], dtype=self.dtype)
        self.ls_ushift['Couple and child'] = ls_ushift
        self.ls_ushift['Female and child'] = ls_ushift

        #self.ls_utilities = np.array([p['uls'],0.0],dtype=self.dtype)
        self.ls_pdown = dict()
        self.ls_pdown['Couple, no children'] = np.array([0.0],
                                                        dtype=self.dtype)
        self.ls_pdown['Female, single'] = np.array([0.0], dtype=self.dtype)
        self.ls_pdown['Male, single'] = np.array([0.0], dtype=self.dtype)
        self.ls_pdown['Female and child'] = ps
        self.ls_pdown['Couple and child'] = ps
        self.nls = dict()
        self.nls['Couple and child'] = len(self.ls_levels['Couple and child'])
        self.nls['Couple, no children'] = len(
            self.ls_levels['Couple, no children'])
        self.nls['Female and child'] = len(self.ls_levels['Female and child'])
        self.nls['Female, single'] = len(self.ls_levels['Female, single'])
        self.nls['Male, single'] = len(self.ls_levels['Male, single'])

        #Cost of Divorce
        if divorce_costs_k == 'Default':
            # by default the costs are set in the bottom
            self.divorce_costs_k = DivorceCosts(
                u_lost_m=self.pars['u_lost_divorce'],
                u_lost_f=self.pars['u_lost_divorce'])
        else:
            if isinstance(divorce_costs_k, dict):
                # you can feed in arguments to DivorceCosts
                self.divorce_costs_k = DivorceCosts(**divorce_costs_k)
            else:
                # or just the output of DivorceCosts
                assert isinstance(divorce_costs_k, DivorceCosts)
                self.divorce_costs_k = divorce_costs_k

        #Cost of Separation
        if divorce_costs_nk == 'Default':
            # by default the costs are set in the bottom
            self.divorce_costs_nk = DivorceCosts(
                u_lost_m=self.pars['u_lost_divorce'],
                u_lost_f=self.pars['u_lost_divorce'])
        else:
            if isinstance(divorce_costs_nk, dict):
                # you can feed in arguments to DivorceCosts
                self.divorce_costs_nk = DivorceCosts(**divorce_costs_nk)
            else:
                # or just the output of DivorceCosts
                assert isinstance(divorce_costs_nk, DivorceCosts)
                self.divorce_costs_nk = divorce_costs_nk

        # exogrid should be deprecated
        if not nogrid:

            exogrid = dict()

            # let's approximate three Markov chains
            # this sets up exogenous grid

            # FIXME: this uses number of points from 0th entry.
            # in principle we can generalize this

            exogrid['zf_t'], exogrid['zf_t_mat'] = rouw_nonst(
                p['T'], p['sig_zf'], p['sig_zf_0'], p['n_zf_t'][0])
            exogrid['zm_t'], exogrid['zm_t_mat'] = rouw_nonst(
                p['T'], p['sig_zm'], p['sig_zm_0'], p['n_zm_t'][0])

            for t in range(Tinc, Tret):
                for key in ['zf_t', 'zf_t_mat', 'zm_t', 'zm_t_mat']:
                    exogrid[key][t] = exogrid[key][Tinc]

            for t in range(Tret, T):
                exogrid['zf_t'][t] = np.array([np.log(p['wret'])])
                exogrid['zm_t'][t] = np.array([np.log(p['wret'])])
                exogrid['zf_t_mat'][t] = np.atleast_2d(1.0)
                exogrid['zm_t_mat'][t] = np.atleast_2d(1.0)

            # fix transition from non-retired to retired
            exogrid['zf_t_mat'][Tret - 1] = np.ones((p['n_zf_t'][Tret - 1], 1))
            exogrid['zm_t_mat'][Tret - 1] = np.ones((p['n_zm_t'][Tret - 1], 1))

            exogrid['psi_t'], exogrid['psi_t_mat'] = rouw_nonst(
                p['T'], p['sigma_psi'], p['sigma_psi_init'], p['n_psi_t'][0])

            zfzm, zfzmmat = combine_matrices_two_lists(exogrid['zf_t'],
                                                       exogrid['zm_t'],
                                                       exogrid['zf_t_mat'],
                                                       exogrid['zm_t_mat'])
            all_t, all_t_mat = combine_matrices_two_lists(
                zfzm, exogrid['psi_t'], zfzmmat, exogrid['psi_t_mat'])
            all_t_mat_sparse_T = [
                sparse.csc_matrix(D.T) if D is not None else None
                for D in all_t_mat
            ]

            #Create a new bad version of transition matrix p(zf_t)

            zf_bad = [
                tauchen_drift(exogrid['zf_t'][t], exogrid['zf_t'][t + 1], 1.0,
                              p['sig_zf'], p['z_drift'])
                for t in range(self.pars['T'] - 1)
            ] + [None]

            #zf_bad = [cut_matrix(exogrid['zf_t_mat'][t]) if t < Tret -1
            #              else (exogrid['zf_t_mat'][t] if t < T - 1 else None)
            #                  for t in range(self.pars['T'])]

            zf_t_mat_down = zf_bad

            zfzm, zfzmmat = combine_matrices_two_lists(exogrid['zf_t'],
                                                       exogrid['zm_t'],
                                                       zf_t_mat_down,
                                                       exogrid['zm_t_mat'])
            all_t_down, all_t_mat_down = combine_matrices_two_lists(
                zfzm, exogrid['psi_t'], zfzmmat, exogrid['psi_t_mat'])
            all_t_mat_down_sparse_T = [
                sparse.csc_matrix(D.T) if D is not None else None
                for D in all_t_mat_down
            ]

            all_t_mat_by_l_nk = [[
                (1 - p) * m + p * md if m is not None else None
                for m, md in zip(all_t_mat, all_t_mat_down)
            ] for p in self.ls_pdown['Couple, no children']]

            all_t_mat_by_l_spt_nk = [[
                (1 - p) * m + p * md if m is not None else None
                for m, md in zip(all_t_mat_sparse_T, all_t_mat_down_sparse_T)
            ] for p in self.ls_pdown['Couple, no children']]

            all_t_mat_by_l_k = [[
                (1 - p) * m + p * md if m is not None else None
                for m, md in zip(all_t_mat, all_t_mat_down)
            ] for p in self.ls_pdown['Couple and child']]

            all_t_mat_by_l_spt_k = [[
                (1 - p) * m + p * md if m is not None else None
                for m, md in zip(all_t_mat_sparse_T, all_t_mat_down_sparse_T)
            ] for p in self.ls_pdown['Couple and child']]

            zf_t_mat_by_l_sk = [[
                (1 - p) * m + p * md if md is not None else None
                for m, md in zip(exogrid['zf_t_mat'], zf_bad)
            ] for p in self.ls_pdown['Female and child']]

            exogrid['all_t_mat_by_l_nk'] = all_t_mat_by_l_nk
            exogrid['all_t_mat_by_l_spt_nk'] = all_t_mat_by_l_spt_nk

            exogrid['all_t_mat_by_l_k'] = all_t_mat_by_l_k
            exogrid['all_t_mat_by_l_spt_k'] = all_t_mat_by_l_spt_k

            exogrid['zf_t_mat_by_l_sk'] = zf_t_mat_by_l_sk

            exogrid['all_t'] = all_t

            Exogrid_nt = namedtuple('Exogrid_nt', exogrid.keys())

            self.exogrid = Exogrid_nt(**exogrid)
            self.pars['nexo_t'] = [v.shape[0] for v in all_t]

            self.compute_child_support_transitions(
                child_support_share=p['child_support_share'])
            #assert False

        #Grid Couple
        self.na = 40
        self.amin = 0
        self.amax = 100
        self.agrid_c = np.linspace(self.amin**0.5,
                                   self.amax**0.5,
                                   self.na,
                                   dtype=self.dtype)**2
        #tune=1.5
        #self.agrid_c = np.geomspace(self.amin+tune,self.amax+tune,num=self.na)-tune

        # this builds finer grid for potential savings
        s_between = 7  # default numer of points between poitns on agrid
        s_da_min = 0.01  # minimal step (does not create more points)
        s_da_max = 0.1  # maximal step (creates more if not enough)

        self.sgrid_c = build_s_grid(self.agrid_c, s_between, s_da_min,
                                    s_da_max)
        self.vsgrid_c = VecOnGrid(self.agrid_c, self.sgrid_c)

        #Grid Single
        self.amin_s = 0
        self.amax_s = self.amax / 2.0
        self.agrid_s = self.agrid_c / 2.0
        #tune_s=1.5
        #self.agrid_s = np.geomspace(self.amin_s+tune_s,self.amax_s+tune_s,num=self.na)-tune_s

        self.sgrid_s = build_s_grid(self.agrid_s, s_between, s_da_min,
                                    s_da_max)
        self.vsgrid_s = VecOnGrid(self.agrid_s, self.sgrid_s)

        # grid for theta
        self.ntheta = 11
        self.thetamin = 0.01
        self.thetamax = 0.99
        self.thetagrid = np.linspace(self.thetamin,
                                     self.thetamax,
                                     self.ntheta,
                                     dtype=self.dtype)

        self.child_a_cost_single = np.minimum(self.agrid_s,
                                              self.pars['child_a_cost'])
        self.child_a_cost_couple = np.minimum(self.agrid_c,
                                              self.pars['child_a_cost'])

        self.vagrid_child_single = VecOnGrid(
            self.agrid_s, self.agrid_s - self.child_a_cost_single)
        self.vagrid_child_couple = VecOnGrid(
            self.agrid_c, self.agrid_c - self.child_a_cost_couple)

        # construct finer grid for bargaining
        ntheta_fine = 10 * self.ntheta  # actual number may be a bit bigger
        self.thetagrid_fine = np.unique(
            np.concatenate((self.thetagrid,
                            np.linspace(self.thetamin,
                                        self.thetamax,
                                        ntheta_fine,
                                        dtype=self.dtype))))
        self.ntheta_fine = self.thetagrid_fine.size

        i_orig = list()

        for theta in self.thetagrid:
            assert theta in self.thetagrid_fine
            i_orig.append(np.where(self.thetagrid_fine == theta)[0])

        assert len(i_orig) == self.thetagrid.size
        # allows to recover original gird points on the fine grid
        self.theta_orig_on_fine = np.array(i_orig).flatten()
        self.v_thetagrid_fine = VecOnGrid(self.thetagrid, self.thetagrid_fine)
        # precomputed object for interpolation

        self.exo_grids = {
            'Female, single': exogrid['zf_t'],
            'Male, single': exogrid['zm_t'],
            'Female and child': exogrid['zf_t'],
            'Couple and child': exogrid['all_t'],
            'Couple, no children': exogrid['all_t']
        }
        self.exo_mats = {
            'Female, single': exogrid['zf_t_mat'],
            'Male, single': exogrid['zm_t_mat'],
            'Female and child': exogrid['zf_t_mat_by_l_sk'],
            'Couple and child': exogrid['all_t_mat_by_l_k'],
            'Couple, no children': exogrid['all_t_mat_by_l_nk']
        }  # sparse version?

        self.utility_shifters = {
            'Female, single': 0.0,
            'Male, single': 0.0,
            'Female and child': p['u_shift_mar'] + p['sm_shift'],
            'Couple and child': p['u_shift_mar'],
            'Couple, no children': p['u_shift_coh']
        }

        # this pre-computes transition matrices for meeting a partner

        try:
            self.partners_distribution_fem = filer('az_dist_fem.pkl',
                                                   0,
                                                   0,
                                                   repeat=False)
            self.partners_distribution_mal = filer('az_dist_mal.pkl',
                                                   0,
                                                   0,
                                                   repeat=False)
        except:
            print('recreating estimates...')
            est_fem = get_estimates(
                fname='income_assets_distribution_male.csv',
                age_start=23,
                age_stop=42,
                zlist=self.exogrid.zm_t[2:])
            filer('az_dist_fem.pkl', est_fem, True, repeat=False)
            self.partners_distribution_fem = est_fem
            est_mal = get_estimates(
                fname='income_assets_distribution_female.csv',
                age_start=21,
                age_stop=40,
                zlist=self.exogrid.zf_t[0:])
            filer('az_dist_mal.pkl', est_mal, True, repeat=False)
            self.partners_distribution_mal = est_mal

        self.build_matches()

        # building m grid
        ezfmin = min([
            np.min(np.exp(g + t))
            for g, t in zip(exogrid['zf_t'], p['f_wage_trend'])
        ])
        ezmmin = min([
            np.min(np.exp(g + t))
            for g, t in zip(exogrid['zm_t'], p['m_wage_trend'])
        ])
        ezfmax = max([
            np.max(np.exp(g + t))
            for g, t in zip(exogrid['zf_t'], p['f_wage_trend'])
        ])
        ezmmax = max([
            np.max(np.exp(g + t))
            for g, t in zip(exogrid['zm_t'], p['m_wage_trend'])
        ])

        self.money_min = 0.95 * min(self.ls_levels['Female and child']) * min(
            ezmmin, ezfmin)  # cause FLS can be up to 0
        mmin = self.money_min
        mmax = ezfmax + ezmmax + np.max(self.pars['R_t']) * self.amax
        mint = (ezfmax + ezmmax)  # poin where more dense grid begins

        ndense = 600
        nm = 1500

        gsparse = np.linspace(mint, mmax, nm - ndense)
        gdense = np.linspace(mmin, mint,
                             ndense + 1)  # +1 as there is a common pt

        self.mgrid = np.zeros(nm, dtype=self.dtype)
        self.mgrid[ndense:] = gsparse
        self.mgrid[:(ndense + 1)] = gdense
        self.mgrid_c = self.mgrid
        self.mgrid_s = self.mgrid
        assert np.all(np.diff(self.mgrid) > 0)

        self.u_precompute()
        self.unplanned_pregnancy_probability()
        self.compute_taxes()
Example #20
0
    def __init__(self,nogrid=False,divorce_costs_k='Default',divorce_costs_nk='Default',**kwargs): 
        
        p = dict()       
        
        
        #age_begin = 21
        #age_data = 23 # age at which the data start being available
        #age_death = 76
        #age_retire = 65
        #age_fertile = 41
        
        
        T = 55
        Tret = 45 # first period when the agent is retired
        Tfert = 20 # first peroid when infertile
        Tdiv = 44 # first period when cannot divorce / renegotiate
        Tmeet = 43 # first period when stop meeting partners
        Tinc = 25 # first period where stop tracking income process and assume it to be fixed
        p['T'] = T
        p['Tret'] = Tret
        p['Tfert'] = Tfert
        p['Tsim'] = T
        p['Tmeet'] = Tmeet
        p['n_zf_t']      = [7]*Tret + [1]*(T-Tret)
        p['n_zm_t']      = [5]*Tret + [1]*(T-Tret)
        p['sigma_psi_init'] = 0.28
        p['mu_psi_init'] = 0.0
        p['sigma_psi']   = 0.11
        p['R_t'] = [1/0.96]*T
        p['n_psi'] = 17
        p['beta_t'] = [0.96]*T
        p['A'] = 1.0 # consumption in couple: c = (1/A)*[c_f^(1+rho) + c_m^(1+rho)]^(1/(1+rho))
        p['crra_power'] = 1.5
        p['couple_rts'] = 0.23    
        p['sig_partner_a'] = 0.1
        p['mu_partner_a_female'] = 0.00
        p['mu_partner_a_male'] = -0.00
        p['dump_factor_z'] = 0.75
        p['sig_partner_z'] = 0.25
        p['mu_partner_z_male'] = -0.02
        p['mu_partner_z_female'] = 0.02
        p['m_bargaining_weight'] = 0.5
        
        
        p['pmeet_21'] = 0.1
        p['pmeet_30'] = 0.2
        p['pmeet_40'] = 0.1
        
        p['pmeet_pre25'] = None
        p['ppreg_pre25'] = None
        
        
        p['pmeet_exo'] = None
        p['ppreg_exo'] = None
        
        p['m_zf'] = 1.0
        p['m_zf0'] = 1.0
        
        
        p['no kids at meeting'] = True
        p['high education'] = True # what trend to pick
        p['any kids'] = True
        
        p['z_drift'] = -0.09 if p['high education'] else -0.06
        
        p['wret'] = 0.8
        p['uls'] = 0.2
        p['pls'] = 1.0
        
        p['income_sd_mult'] = 1.0
        p['pay_gap'] = True
        
        
        p['preg_mult'] = 1.0
        
        p['u_shift_mar'] = 0.0
        p['u_shift_coh'] = 0.0
        p['sm_shift'] = 0.0
        
        p['disutil_marry_sm_fem'] = 0.0
        p['disutil_marry_sm_mal'] = 10.0
        p['disutil_shotgun'] = 2.0
        p['pmeet_multiplier_fem'] = 1.0
        p['p_to_meet_sm_if_mal'] = 0.1
        
        p['taste_shock_mult'] = 1.0
        
        p['p_abortion_access'] = 0.5
        p['abortion_costs'] = 10.0
        
        p['u_lost_divorce'] = 0.0 
       
        
        p['child_a_cost'] = 0.0
        
        p['child_support_share'] = 0.2  
        p['child_support_awarded_nm'] = 0.284
        p['child_support_awarded_div'] = 0.461
            
        
        
        p['util_lam'] = 0.7
        p['util_alp'] = 0.5
        p['util_xi'] = 1.5
        p['util_kap'] = 0.5
        p['util_qbar'] = 0.0
        
        p['util_out_lf'] = 0.0
        
        p['ppreg_sim_mult'] = 1.0
        
        
        p['tax_childless_couples'] = True        
        p['tax_couples_woth_children'] = True
        p['tax_single_mothers'] = True
        
        p['preg_21'] = 0.01
        p['preg_28'] = 0.5
        p['preg_35'] = 0.3
        
        
        
        for key, value in kwargs.items():
            assert (key in p), 'wrong name?'
            p[key] = value
        
        
        
        
        if p['high education']:            
            p['sig_zm']    = p['income_sd_mult']*0.16138593
            p['sig_zm_0']  = p['income_sd_mult']*0.41966813 
            
            p['sig_zf']    = p['income_sd_mult']*p['m_zf']*0.19571624
            p['sig_zf_0']  = p['income_sd_mult']*p['m_zf0']*0.43351219
        else:
            p['sig_zm']    = p['income_sd_mult']*0.17195085
            p['sig_zm_0']  = p['income_sd_mult']*0.2268650
            
            
            p['sig_zf']    = p['income_sd_mult']*p['m_zf']*0.1762148
            p['sig_zf_0']  = p['income_sd_mult']*p['m_zf0']*0.1762148
            
        
        p['sm_init'] = (0.02 if p['high education'] else 0.25) if p['any kids'] else 0.0 # initial share of single moms
        
        
        # college
        
        if p['high education']: 
            
            
            m_trend_data = [3.3899509,3.5031169,3.6395543,3.7025571,3.7646729,3.8168057,3.8706297,3.9168266,3.9628897,3.999365,4.0401083,4.0584735,4.114779,4.1344154,4.1570761,4.1703323,4.189479,4.1908761,4.2096766,4.2190858,4.2284456,4.2330354,4.2364118,4.2443216,4.2449467]
            
            
            f_trend_data = [3.0796507,3.1468105,3.2911468,3.4024807,3.5485041,3.6067711,3.662677,3.7131363,3.7598972,3.794152,3.8221141,3.8500988,3.8791148,3.8860543,3.9176438,3.9332115,3.9479731,3.9537252,3.9578583,3.9480597,3.9559513,3.9562479,3.9585957,3.9576563,3.9589798]

            
            
            nm = len(m_trend_data)-1
            nf = len(f_trend_data)-1
            
            
            p['m_wage_trend'] = np.array(
                                        [m_trend_data[min(t,nm)]
                                             for t in range(T)]
                                        )
            p['f_wage_trend'] = np.array(
                                [f_trend_data[min(t,nf)]
                                             for t in range(T)]
                                        )
            
        else:
        # no college
            m_trend_data = [3.091856,3.130104,3.2259613,3.2581962,3.2772099,3.2999002,3.3206571,3.3314928,3.3573047,3.3663062,3.3801406,3.3909449,3.4160915,3.4358479,3.4488938,3.4534575,3.4654005,3.4655065,3.4815268,3.4859583,3.4967845,3.4972438,3.5118738,3.525121,3.5271331]
            
            
            f_trend_data = [2.9056071,2.9427025,2.9773922,2.999882,3.0932755,3.1129375,3.1199322,3.1352323,3.1498192,3.1606338,3.168912,3.1722982,3.1775691,3.1831384,3.2040837,3.1997439,3.2122542,3.2085543,3.2209876,3.2232882,3.2273824,3.2336534,3.233879,3.244275,3.2527455]

            
            
            nm = len(m_trend_data)-1
            nf = len(f_trend_data)-1
            
            
            p['m_wage_trend'] = np.array(
                                        [m_trend_data[min(t,nm)]
                                             for t in range(T)]
                                        )
            p['f_wage_trend'] = np.array(
                                [f_trend_data[min(t,nf)]
                                             for t in range(T)]
                                        )
            
        
        
        if not p['pay_gap']:
            p['sig_zf'], p['sig_zf_0'] = p['sig_zm'], p['sig_zm_0']
            p['f_wage_trend'] = p['m_wage_trend']
        
        
        p['preg_az'] =  0.00
        p['preg_azt'] = 0.00
        
        #Get the probability of meeting, adjusting for year-period
           
        
        p['taste_shock'] = 0.0 #*p['taste_shock_mult']*0.0#p['sigma_psi']
        
        p['is fertile'] = [p['any kids']]*Tfert + [False]*(T-Tfert)
        p['can divorce'] = [True]*Tdiv + [False]*(T-Tdiv)        
        #p['poutsm_t'] = [p['poutsm']]*T
        
        
        
        p['pmeet_0'],  p['pmeet_t'], p['pmeet_t2'] = prob_polyfit(
                    (p['pmeet_21'],0),(p['pmeet_30'],9),(p['pmeet_40'],19),
                                                                   max_power=2)
        
        p['preg_a0'],  p['preg_at'], p['preg_at2'] = prob_polyfit(
                    (p['preg_21'],0),(p['preg_28'],7),(p['preg_35'],14),
                                                                   max_power=2)
        
        if p['pmeet_exo'] is None:
            p['pmeet_t'] = [np.clip(p['pmeet_0'] + t*p['pmeet_t'] + (t**2)*p['pmeet_t2'],0.0,1.0) for t in range(20)] + \
                        [p['pmeet_40']]*(Tmeet - 20) + [0.0]*(T-Tmeet)
            
            p['pmeet_t'] = np.array(p['pmeet_t'])
                        
            if p['pmeet_pre25'] is not None: p['pmeet_t'][:4] = p['pmeet_pre25']
        else:
            p['pmeet_t'] = [p['pmeet_exo'][min(t,p['pmeet_exo'].size-1)] for t in range(Tmeet)] + [0.0]*(T-Tmeet)
            p['pmeet_t'] = np.array(p['pmeet_t'])
        
        
        p['n_psi_t'] = [p['n_psi']]*T
        
        p['psi_clip'] = 8.5*p['sigma_psi_init']
        
        
        
        p['fert_prob_t'] = [0.86*(t<=3) + 0.78*(t>3 and t<=8) + 
                            0.63*(t>=9 and t<=13) + 0.52*(t>=14) for
                            t in range(T)]
        
        #p['fert_prob_t'] = [1.0]*T
        
        
        
        self.pars = p
        
        self.dtype = np.float64 # type for all floats
        
       
        
        # relevant for integration
        self.state_names = ['Female, single','Male, single','Female and child','Couple, no children','Couple and child']
        
        # female labor supply
        
        lmin = 0.2
        lmax = 1.0
        nl = 2
        
        ls = np.array([0.2,1.0]) #np.linspace(lmin,lmax,nl,dtype=self.dtype)
        ps = np.array([p['pls'],0.0])
        ls_ushift = np.array([p['util_out_lf'],0.0])
        
        
        self.ls_levels = dict()
        self.ls_levels['Couple, no children'] = np.array([1.0],dtype=self.dtype)
        self.ls_levels['Female, single'] = np.array([1.0],dtype=self.dtype)
        self.ls_levels['Male, single'] = np.array([1.0],dtype=self.dtype)
        self.ls_levels['Couple and child'] = ls
        self.ls_levels['Female and child'] = ls
        
        self.ls_ushift = dict()
        self.ls_ushift['Couple, no children'] = np.array([0.0],dtype=self.dtype)
        self.ls_ushift['Female, single'] = np.array([0.0],dtype=self.dtype)
        self.ls_ushift['Male, single'] = np.array([0.0],dtype=self.dtype)
        self.ls_ushift['Couple and child'] = ls_ushift
        self.ls_ushift['Female and child'] = ls_ushift
        
        
        
        
        
        
        #self.ls_utilities = np.array([p['uls'],0.0],dtype=self.dtype)
        self.ls_pdown = dict()
        self.ls_pdown['Couple, no children'] = np.array([0.0],dtype=self.dtype)
        self.ls_pdown['Female, single'] = np.array([0.0],dtype=self.dtype)
        self.ls_pdown['Male, single']   = np.array([0.0],dtype=self.dtype)
        self.ls_pdown['Female and child'] = ps
        self.ls_pdown['Couple and child'] = ps
        self.nls = dict()
        self.nls['Couple and child'] = len(self.ls_levels['Couple and child'])
        self.nls['Couple, no children'] = len(self.ls_levels['Couple, no children'])
        self.nls['Female and child'] = len(self.ls_levels['Female and child'])
        self.nls['Female, single'] = len(self.ls_levels['Female, single'])
        self.nls['Male, single'] = len(self.ls_levels['Male, single'])
        
        
        
        
        #Cost of Divorce
        if divorce_costs_k == 'Default':
            # by default the costs are set in the bottom
            self.divorce_costs_k = DivorceCosts(u_lost_m=self.pars['u_lost_divorce'],
                                                u_lost_f=self.pars['u_lost_divorce'])
        else:
            if isinstance(divorce_costs_k,dict):
                # you can feed in arguments to DivorceCosts
                self.divorce_costs_k = DivorceCosts(**divorce_costs_k)
            else:
                # or just the output of DivorceCosts
                assert isinstance(divorce_costs_k,DivorceCosts)
                self.divorce_costs_k = divorce_costs_k
                
        #Cost of Separation
        if divorce_costs_nk == 'Default':
            # by default the costs are set in the bottom
            self.divorce_costs_nk = DivorceCosts(u_lost_m=self.pars['u_lost_divorce'],
                                              u_lost_f=self.pars['u_lost_divorce'])
        else:
            if isinstance(divorce_costs_nk,dict):
                # you can feed in arguments to DivorceCosts
                self.divorce_costs_nk = DivorceCosts(**divorce_costs_nk)
            else:
                # or just the output of DivorceCosts
                assert isinstance(divorce_costs_nk,DivorceCosts)
                self.divorce_costs_nk = divorce_costs_nk
            
        # exogrid should be deprecated
        if not nogrid:
        
            exogrid = dict()
            
            
            # let's approximate three Markov chains
            # this sets up exogenous grid
            
            # FIXME: this uses number of points from 0th entry. 
            # in principle we can generalize this
            
            exogrid['zf_t'],  exogrid['zf_t_mat'] = rouw_nonst(p['T'],p['sig_zf'],p['sig_zf_0'],p['n_zf_t'][0])
            exogrid['zm_t'],  exogrid['zm_t_mat'] = rouw_nonst(p['T'],p['sig_zm'],p['sig_zm_0'],p['n_zm_t'][0])
            
            
            for t in range(Tinc,Tret):
                for key in ['zf_t','zf_t_mat','zm_t','zm_t_mat']:
                    exogrid[key][t] = exogrid[key][Tinc]
            
            
            for t in range(Tret,T):
                exogrid['zf_t'][t] = np.array([np.log(p['wret'])])
                exogrid['zm_t'][t] = np.array([np.log(p['wret'])])
                exogrid['zf_t_mat'][t] = np.atleast_2d(1.0)
                exogrid['zm_t_mat'][t] = np.atleast_2d(1.0)
                
                
            # fix transition from non-retired to retired    
            exogrid['zf_t_mat'][Tret-1] = np.ones((p['n_zf_t'][Tret-1],1))
            exogrid['zm_t_mat'][Tret-1] = np.ones((p['n_zm_t'][Tret-1],1))
            
            exogrid['psi_t'], exogrid['psi_t_mat'] = rouw_nonst(p['T'],p['sigma_psi'],p['sigma_psi_init'],p['n_psi_t'][0])
            #exogrid['psi_t'], exogrid['psi_t_mat'] = tauchen_nonst(p['T'],p['sigma_psi'],p['sigma_psi_init'],p['n_psi_t'][0],nsd=2.5,fix_0=False)
            #assert False
            zfzm, zfzmmat = combine_matrices_two_lists(exogrid['zf_t'], exogrid['zm_t'], exogrid['zf_t_mat'], exogrid['zm_t_mat'])
            all_t, all_t_mat = combine_matrices_two_lists(zfzm,exogrid['psi_t'],zfzmmat,exogrid['psi_t_mat'])
            all_t_mat_sparse_T = [sparse.csc_matrix(D.T) if D is not None else None for D in all_t_mat]
            
            
            #Create a new bad version of transition matrix p(zf_t)
            
            
            
            zf_bad = [tauchen_drift(exogrid['zf_t'][t], exogrid['zf_t'][t+1], 
                                    1.0, p['sig_zf'], p['z_drift'])
                        for t in range(self.pars['T']-1) ] + [None]
            
            
            #zf_bad = [cut_matrix(exogrid['zf_t_mat'][t]) if t < Tret -1 
            #              else (exogrid['zf_t_mat'][t] if t < T - 1 else None) 
            #                  for t in range(self.pars['T'])]
            
            zf_t_mat_down = zf_bad
            
            zfzm, zfzmmat = combine_matrices_two_lists(exogrid['zf_t'], exogrid['zm_t'], zf_t_mat_down, exogrid['zm_t_mat'])
            all_t_down, all_t_mat_down = combine_matrices_two_lists(zfzm,exogrid['psi_t'],zfzmmat,exogrid['psi_t_mat'])
            all_t_mat_down_sparse_T = [sparse.csc_matrix(D.T) if D is not None else None for D in all_t_mat_down]
            
            
            
            all_t_mat_by_l_nk = [ [(1-p)*m + p*md if m is not None else None 
                                for m , md in zip(all_t_mat,all_t_mat_down)]
                               for p in self.ls_pdown['Couple, no children'] ]
            
            all_t_mat_by_l_spt_nk = [ [(1-p)*m + p*md if m is not None else None
                                    for m, md in zip(all_t_mat_sparse_T,all_t_mat_down_sparse_T)]
                               for p in self.ls_pdown['Couple, no children'] ]
            
            all_t_mat_by_l_k = [ [(1-p)*m + p*md if m is not None else None 
                                for m , md in zip(all_t_mat,all_t_mat_down)]
                               for p in self.ls_pdown['Couple and child'] ]
            
            all_t_mat_by_l_spt_k = [ [(1-p)*m + p*md if m is not None else None
                                    for m, md in zip(all_t_mat_sparse_T,all_t_mat_down_sparse_T)]
                               for p in self.ls_pdown['Couple and child'] ]
            
            zf_t_mat_by_l_sk = [ [(1-p)*m + p*md if md is not None else None 
                                for m , md in zip(exogrid['zf_t_mat'],zf_bad)]
                                   for p in self.ls_pdown['Female and child'] ]
            
            
            exogrid['all_t_mat_by_l_nk'] = all_t_mat_by_l_nk
            exogrid['all_t_mat_by_l_spt_nk'] = all_t_mat_by_l_spt_nk
            
            exogrid['all_t_mat_by_l_k'] = all_t_mat_by_l_k
            exogrid['all_t_mat_by_l_spt_k'] = all_t_mat_by_l_spt_k
            
            exogrid['zf_t_mat_by_l_sk'] = zf_t_mat_by_l_sk
            
            exogrid['all_t'] = all_t
            
            Exogrid_nt = namedtuple('Exogrid_nt',exogrid.keys())
            
            self.exogrid = Exogrid_nt(**exogrid)
            self.pars['nexo_t'] = [v.shape[0] for v in all_t]
            
            self.compute_child_support_transitions(child_support_share=p['child_support_share'])
            #assert False
            
            
            
        #Grid Couple
        self.na = 40
        self.amin = 0
        self.amax = 1000.0
        self.agrid_c = np.linspace(self.amin**0.5,self.amax**0.5,self.na,dtype=self.dtype)**2
        #tune=1.5
        #self.agrid_c = np.geomspace(self.amin+tune,self.amax+tune,num=self.na)-tune
        
        # this builds finer grid for potential savings
        s_between = 7 # default numer of points between poitns on agrid
        s_da_min = 0.2 # minimal step (does not create more points)
        s_da_max = 10.0 # maximal step (creates more if not enough)
        
        self.sgrid_c = build_s_grid(self.agrid_c,s_between,s_da_min,s_da_max)
        self.vsgrid_c = VecOnGrid(self.agrid_c,self.sgrid_c)
        
        
         
        #Grid Single
        self.amin_s = 0
        self.amax_s = self.amax/2.0
        self.agrid_s = self.agrid_c/2.0
        #tune_s=1.5
        #self.agrid_s = np.geomspace(self.amin_s+tune_s,self.amax_s+tune_s,num=self.na)-tune_s
        
        self.sgrid_s = build_s_grid(self.agrid_s,s_between,s_da_min,s_da_max)
        self.vsgrid_s = VecOnGrid(self.agrid_s,self.sgrid_s)
        
        # grid for theta
        self.ntheta = 11
        self.thetamin = 0.05
        self.thetamax = 0.95
        self.thetagrid = np.linspace(self.thetamin,self.thetamax,self.ntheta,dtype=self.dtype)
        
        
        self.child_a_cost_single = np.minimum(self.agrid_s,self.pars['child_a_cost'])
        self.child_a_cost_couple = np.minimum(self.agrid_c,self.pars['child_a_cost'])
        
        assert self.pars['child_a_cost']<1e-3, 'not implemented'
        #self.vagrid_child_single = VecOnGrid(self.agrid_s, self.agrid_s - self.child_a_cost_single)
        #self.vagrid_child_couple = VecOnGrid(self.agrid_c, self.agrid_c - self.child_a_cost_couple)
        
        
        # construct finer grid for bargaining
        ntheta_fine = 10*self.ntheta # actual number may be a bit bigger
        self.thetagrid_fine = np.unique(np.concatenate( (self.thetagrid,np.linspace(self.thetamin,self.thetamax,ntheta_fine,dtype=self.dtype)) ))
        self.ntheta_fine = self.thetagrid_fine.size
        
        i_orig = list()
        
        for theta in self.thetagrid:
            assert theta in self.thetagrid_fine
            i_orig.append(np.where(self.thetagrid_fine==theta)[0])
            
        assert len(i_orig) == self.thetagrid.size
        # allows to recover original gird points on the fine grid        
        self.theta_orig_on_fine = np.array(i_orig).flatten()
        self.v_thetagrid_fine = VecOnGrid(self.thetagrid,self.thetagrid_fine)
        # precomputed object for interpolation

            
        
        


        self.exo_grids = {'Female, single':exogrid['zf_t'],
                          'Male, single':exogrid['zm_t'],
                          'Female and child':exogrid['zf_t'],
                          'Couple and child':exogrid['all_t'],
                          'Couple, no children':exogrid['all_t']}
        self.exo_mats = {'Female, single':exogrid['zf_t_mat'],
                          'Male, single':exogrid['zm_t_mat'],
                          'Female and child':exogrid['zf_t_mat_by_l_sk'],
                          'Couple and child':exogrid['all_t_mat_by_l_k'],
                          'Couple, no children':exogrid['all_t_mat_by_l_nk']} # sparse version?
        
        
        self.utility_shifters = {'Female, single':0.0,
                                 'Male, single':0.0,
                                 'Female and child':p['u_shift_mar'] + p['sm_shift'],
                                 'Couple and child':p['u_shift_mar'],
                                 'Couple, no children':p['u_shift_coh']}
        
        
        # this pre-computes transition matrices for meeting a partner
       
        
        name_fem_pkl = 'az_dist_fem.pkl' if p['high education'] else 'az_dist_fem_noc.pkl'
        name_mal_pkl = 'az_dist_mal.pkl' if p['high education'] else 'az_dist_mal_noc.pkl'
        name_fem_csv = 'income_assets_distribution_male_col.csv' if p['high education'] else 'income_assets_distribution_male_hs.csv'
        name_mal_csv = 'income_assets_distribution_female_col.csv' if p['high education'] else 'income_assets_distribution_female_hs.csv'
        # this is not an error, things are switched
        
        
        try:
            self.partners_distribution_fem = filer(name_fem_pkl,0,0,repeat=False)
            self.partners_distribution_mal = filer(name_mal_pkl,0,0,repeat=False)
        except:
            print('recreating estimates...')
            
            est_fem = get_estimates(fname=name_fem_csv,
                                    age_start=23,age_stop=42,
                                    zlist=self.exogrid.zm_t[2:],
                                    female=False,college=p['high education'])
            filer(name_fem_pkl,est_fem,True,repeat=False)
            self.partners_distribution_fem = est_fem
            est_mal = get_estimates(fname=name_mal_csv,
                                    age_start=21,age_stop=40,
                                    zlist=self.exogrid.zf_t[0:],
                                    female=True,college=p['high education'])
            filer(name_mal_pkl,est_mal,True,repeat=False)
            self.partners_distribution_mal = est_mal
            
        self.build_matches()
        
        # building m grid
        ezfmin = min([np.min(np.exp(g+t)) for g,t in zip(exogrid['zf_t'],p['f_wage_trend'])])
        ezmmin = min([np.min(np.exp(g+t)) for g,t in zip(exogrid['zm_t'],p['m_wage_trend'])])
        ezfmax = max([np.max(np.exp(g+t)) for g,t in zip(exogrid['zf_t'],p['f_wage_trend'])])
        ezmmax = max([np.max(np.exp(g+t)) for g,t in zip(exogrid['zm_t'],p['m_wage_trend'])])
        
        self.money_min = 0.95*min(self.ls_levels['Female and child'])*min(ezmmin,ezfmin) # cause FLS can be up to 0
        mmin = self.money_min
        mmax = ezfmax + ezmmax + np.max(self.pars['R_t'])*self.amax
        mint = (ezfmax + ezmmax) # poin where more dense grid begins
        
        ndense = 1900
        nm = 3500
        
        gsparse = np.linspace(mint,mmax,nm-ndense)
        gdense = np.linspace(mmin,mint,ndense+1) # +1 as there is a common pt
        
        self.mgrid = np.zeros(nm,dtype=self.dtype)
        self.mgrid[ndense:] = gsparse
        self.mgrid[:(ndense+1)] = gdense
        self.mgrid_c = self.mgrid
        self.mgrid_s = self.mgrid
        assert np.all(np.diff(self.mgrid)>0)
        
        self.u_precompute()
        self.unplanned_pregnancy_probability()
        self.compute_taxes()
        
        self.cupyfy()
Example #21
0
    def anext(self, t):
        # finds savings (potenitally off-grid)

        for ipol in range(self.npol):
            for ist, sname in enumerate(self.state_codes):

                is_state_any_pol = (self.state[:, t] == ist)
                is_pol = (self.policy_ind[:, t] == ipol)

                is_state = (is_state_any_pol) & (is_pol)

                use_theta = self.has_theta[ist]
                nst = np.sum(is_state)

                if nst == 0:
                    continue

                ind = np.where(is_state)[0]

                #pol = self.Mlist[ipol].decisions[t][sname]
                vpol = self.Mlist[ipol].V[t][sname]

                if not use_theta:

                    # apply for singles

                    anext = vpol['s'][self.iassets[ind, t], self.iexo[ind, t]]
                    self.iassets[ind, t + 1] = VecOnGrid(
                        self.setup.agrid_s,
                        anext).roll(shocks=self._shocks_single_a[ind, t])
                    self.s[ind, t] = anext
                    self.c[ind, t] = vpol['c'][self.iassets[ind, t],
                                               self.iexo[ind, t]]
                    self.x[ind, t] = vpol['x'][self.iassets[ind, t],
                                               self.iexo[ind, t]]
                    if np.any(self.iassets[ind, t + 1] == self.setup.na - 1):
                        self.ub_hit_single = True

                else:

                    # interpolate in both assets and theta
                    # function apply_2dim is experimental but I checked it at this setup

                    # apply for couples

                    def tint(x):
                        return self.tht_interpolate(
                            x, (self.iassets[ind, t], self.iexo[ind, t]),
                            self.itheta[ind, t])

                    anext = tint(vpol['s'])
                    self.s[ind, t] = anext

                    self.x[ind, t] = tint(vpol['x'])
                    self.c[ind, t] = tint(vpol['c'])

                    self.iassets[ind, t + 1] = VecOnGrid(
                        self.setup.agrid_c,
                        anext).roll(shocks=self._shocks_couple_a[ind, t])
                    if np.any(self.iassets[ind, t + 1] == self.setup.na - 1):
                        self.couple = True

                assert np.all(anext >= 0)