def tauchen_nonst(T=40, sigma_persistent=0.05, sigma_init=0.2, npts=50, *, nsd=3, fix_0=False): import numpy as np # start with creating list of points sd_z = sd_rw(T, sigma_persistent, sigma_init) X = list() Pi = list() for t in range(0, T): s = t if not fix_0 else 0 #X = X + [np.linspace(-nsd*sd_z[s],nsd*sd_z[s],num=npts)] X = X + [nonuniform_centered_grid(npts, nsd * sd_z[s])] # then define a list transition matrices # note that Pi[t] is transition matrix from X[t] to X[t+1] for t in range(1, T): ''' h = 2*nsd*sd_z[t]/(npts-1) # step size Pi_here = np.zeros([npts,npts]) Pi_here_2 = np.zeros([npts,npts]) Pi_here[:,0] = normcdf_tr( (X[t][0] - X[t-1][:] + h/2) / sigma_persistent) Pi_here[:,-1] = 1.0 - normcdf_tr( (X[t][-1] - X[t-1][:] - h/2) / sigma_persistent) for i in range(1,npts-1): Pi_here[:,i] = normcdf_tr( (X[t][i] - X[t-1][:] + h/2) / sigma_persistent) - normcdf_tr( (X[t][i] - X[t-1][:] - h/2) / sigma_persistent) #print(Pi_here) #print(np.sum(Pi_here,axis=1)) #assert(np.all( abs( np.sum(Pi_here,axis=1) -np.ones(npts) ) < 1e-6 )) ''' Pi_here = np.zeros((npts, npts)) for i in range(npts): Pi_here[i, :] = int_prob(X[t], X[t - 1][i], sigma_persistent, trim=False) Pi = Pi + [Pi_here] assert np.allclose(Pi_here.sum(axis=1), 1.0) Pi = Pi + [None] # last matrix is not defined return X, Pi
def mar_mats_assets(self,npoints=4,abar=0.1): # for each grid point on single's grid it returns npoints positions # on (potential) couple's grid's and assets of potential partner # (that can be off grid) and correpsonding probabilities. self.prob_a_mat = dict() self.i_a_mat = dict() na = self.agrid_s.size agrid_s = self.agrid_s agrid_c = self.agrid_c s_a_partner = self.pars['sig_partner_a'] for female in [True,False]: prob_a_mat = np.zeros((na,npoints),dtype=self.dtype) i_a_mat = np.zeros((na,npoints),dtype=np.int16) for ia, a in enumerate(agrid_s): lagrid_t = np.zeros_like(agrid_c) i_neg = (agrid_c <= max(abar,a) - 1e-6) # if a is zero this works a bit weird but does the job lagrid_t[~i_neg] = np.log(2e-6 + (agrid_c[~i_neg] - a)/max(abar,a)) lmin = lagrid_t[~i_neg].min() # just fill with very negative values so this is never chosen lagrid_t[i_neg] = lmin - s_a_partner*10 - \ s_a_partner*np.flip(np.arange(i_neg.sum())) # TODO: this needs to be checked if female: mean=self.pars['mean_partner_a_female'] else: mean=self.pars['mean_partner_a_male'] p_a = int_prob(lagrid_t,mu=mean,sig=s_a_partner,n_points=npoints) i_pa = (-p_a).argsort()[:npoints] # this is more robust then nonzero p_pa = p_a[i_pa] prob_a_mat[ia,:] = p_pa i_a_mat[ia,:] = i_pa self.prob_a_mat[female] = prob_a_mat self.i_a_mat[female] = i_a_mat
def nonpar_distribution(z, t, data, nbins, *, female, college): if female and college: sd = lambda t: np.exp(1.153647 - .013831 * t) mu = lambda z, t: .496482 + .2205861 * t + .888435 * z + .0874937 * z * t if (not female) and college: sd = lambda t: np.exp(1.056854 - .0106599 * t) mu = lambda z, t: 1.066965 + .2004596 * t + 1.309432 * z + .0649392 * z * t if (female) and (not college): sd = lambda t: np.exp(.8055752 + .0050402 * t) mu = lambda z, t: .5254092 + .0645352 * t + 1.717792 * z + .0047126 * z * t if (not female) and (not college): sd = lambda t: np.exp(1.173178 - .0113634 * t) mu = lambda z, t: .5254092 + .0645352 * t + 1.717792 * z + .0047126 * z * t vec = z vm = np.concatenate(([-np.inf], vec[:-1])) vp = np.concatenate((vec[1:], [np.inf])) z_up = 0.5 * (vec + vp) z_down = 0.5 * (vec + vm) nz = z.size z_probs = np.zeros((z.size, )) a_probs_by_z = np.zeros((z.size, nbins)) a_vals_by_z = np.zeros((z.size, nbins)) if nbins == 5: sa = np.array([-1.5, -0.75, 0.0, 0.75, 1.5]) else: sa = sps.norm.ppf([(i + 1) / (nbins + 1) for i in range(nbins)]) muz = np.average(data['z'], weights=data['w']) sdz = np.sqrt(np.average((data['z'] - muz)**2, weights=data['w'])) pz_all = int_prob(vec, mu=muz, sig=sdz) for iz in range(nz): p_groups = np.zeros((nbins)) a_vals = np.zeros((nbins)) p_z = pz_all[iz] vsd = sd(t) vmu = mu(z[iz], t) la_vals = vmu + sa * vsd p_groups = int_prob(la_vals, mu=vmu, sig=vsd, trim=False) a_vals = np.exp(la_vals) * (la_vals >= 0) z_probs[iz] = p_z a_probs_by_z[iz, :] = p_groups a_vals_by_z[iz, :] = a_vals #assert np.all(np.diff(a_vals)>=0) assert np.allclose(np.sum(p_groups), 1.0) assert np.allclose(z_probs.sum(), 1.0) return z_probs, a_probs_by_z, a_vals_by_z
def _mar_mats(self,t,female=True): # this returns transition matrix for single agents into possible couples # rows are single's states # columnts are couple's states # you have to transpose it if you want to use it for integration setup = self nexo = setup.pars['nexo_t'][t] sigma_psi_init = setup.pars['sigma_psi_init'] psi_couple = setup.exogrid.psi_t[t+1] mu = setup.pars['mu_psi_init'] if female: nz_single = setup.exogrid.zf_t[t].shape[0] p_mat = np.empty((nz_single,nexo)) z_own = setup.exogrid.zf_t[t] n_zown = z_own.shape[0] z_partner = setup.exogrid.zm_t[t+1] zmat_own = setup.exogrid.zf_t_mat[t] pz_precomputed = self.partners_distribution_fem else: nz_single = setup.exogrid.zm_t[t].shape[0] p_mat = np.empty((nz_single,nexo)) z_own = setup.exogrid.zm_t[t] n_zown = z_own.shape[0] z_partner = setup.exogrid.zf_t[t+1] zmat_own = setup.exogrid.zm_t_mat[t] pz_precomputed = self.partners_distribution_mal def ind_conv(a,b,c): return setup.all_indices(t,(a,b,c))[0] pz_all = pz_precomputed['prob_z'] pick = t if t < len(pz_all) else -1 pz = pz_precomputed['prob_z'][pick] pa = pz_precomputed['prob_a_by_z'][pick] va = pz_precomputed['val_a_by_z'][pick] na_matches = pa.shape[-1] p_mat_pa = np.empty((nexo,) + (na_matches,),dtype=setup.dtype) p_mat_ia = np.empty((nexo,) + (setup.na,) + (na_matches,),dtype=np.int16) agrid_s = self.agrid_s for iz in range(n_zown): p_psi = int_prob(psi_couple,mu=mu,sig=sigma_psi_init) if female: p_zm = np.array(pz) p_zf = zmat_own[iz,:] else: p_zf = np.array(pz) p_zm = zmat_own[iz,:] p_vec = np.zeros(nexo) ie, izf, izm, ipsi = setup.all_indices(t) izpnext = izm if female else izf p_vec = p_zm[izm]*p_zf[izf]*p_psi[ipsi] assert np.allclose(p_vec.sum(),1.0) p_mat[iz,:] = p_vec a_resuling = np.clip(agrid_s[None,:,None] + va[:,None,:],0.0,self.agrid_c.max()-1e-5) ia_resulting = np.clip(np.searchsorted(setup.agrid_c,a_resuling) - 1,0,setup.na-1) p_resulting = pa assert np.allclose(pa.sum(axis=-1),1.0) # this slightly brings things down p_mat_ia[:,:,:] = ia_resulting[izpnext,:,:] p_mat_pa[:,:] = p_resulting[izpnext,:] nexo_ext = nexo*na_matches p_exo_ext = np.empty((nz_single,nexo_ext),dtype=self.dtype) ia_table = np.empty((setup.na,nexo_ext),dtype=np.int16) # resulting ia for each na and nexo_ext corresponding_iexo = np.empty(nexo_ext,dtype=np.int16) corresponding_imatch = np.empty(nexo_ext,dtype=np.int8) for im in range(na_matches): p_exo_ext[:,(im*nexo):((im+1)*nexo)] = p_mat*(p_mat_pa[:,im][None,:]) ia_table[:,(im*nexo):((im+1)*nexo)] = p_mat_ia[:,:,im].T corresponding_iexo[(im*nexo):((im+1)*nexo)] = ie corresponding_imatch[(im*nexo):((im+1)*nexo)] = im return {'p_mat_iexo':p_mat, 'p_mat_extended':p_exo_ext, 'ia_c_table':ia_table, 'corresponding_iexo':corresponding_iexo, 'corresponding_imatch':corresponding_imatch}
def mar_mats_iexo(self,t,female=True,trim_lvl=0.001): # TODO: check timing # this returns transition matrix for single agents into possible couples # rows are single's states # columnts are couple's states # you have to transpose it if you want to use it for integration setup = self nexo = setup.pars['nexo_t'][t] sigma_psi_init = setup.pars['sigma_psi_init'] #sig_z_partner = setup.pars['sig_partner_z'] psi_couple = setup.exogrid.psi_t[t+1] if female: nz_single = setup.exogrid.zf_t[t].shape[0] p_mat = np.empty((nexo,nz_single)) z_own = setup.exogrid.zf_t[t] n_zown = z_own.shape[0] z_partner = setup.exogrid.zm_t[t] zmat_own = setup.exogrid.zf_t_mat[t] trend=setup.pars['m_wage_trend_single'][t] mean=setup.pars['mean_partner_z_female']-setup.pars['m_wage_trend'][t]+setup.pars['m_wage_trend_single'][t] sig_z_partner=(setup.pars['sig_zm_0']**2+(t+1)*setup.pars['sig_zm']**2)**0.5 else: nz_single = setup.exogrid.zm_t[t].shape[0] p_mat = np.empty((nexo,nz_single)) z_own = setup.exogrid.zm_t[t] n_zown = z_own.shape[0] z_partner = setup.exogrid.zf_t[t] zmat_own = setup.exogrid.zm_t_mat[t] trend=setup.pars['f_wage_trend_single'][t] mean=setup.pars['mean_partner_z_male']-setup.pars['f_wage_trend'][t]+setup.pars['f_wage_trend_single'][t] sig_z_partner=(setup.pars['sig_zf_0']**2+(t+1)*setup.pars['sig_zf']**2)**0.5 def ind_conv(a,b,c): return setup.all_indices(t,(a,b,c))[0] for iz in range(n_zown): p_psi = int_prob(psi_couple,mu=0,sig=sigma_psi_init) if female: p_zm = int_prob(z_partner, mu=setup.pars['dump_factor_z']*z_partner[iz]+ mean+setup.pars['mean_partner_z_female'],sig=(1-setup.pars['dump_factor_z'])** 0.5*sig_z_partner*setup.pars['sig_partner_mult']) p_zf = zmat_own[iz,:] else: p_zf = int_prob(z_partner, mu=setup.pars['dump_factor_z']*z_partner[iz]+ mean+setup.pars['mean_partner_z_male'],sig=(1-setup.pars['dump_factor_z'])** 0.5*sig_z_partner*setup.pars['sig_partner_mult']) p_zm = zmat_own[iz,:] #sm = sf p_vec = np.zeros(nexo) for izf, p_zf_i in enumerate(p_zf): if p_zf_i < trim_lvl: continue for izm, p_zm_i in enumerate(p_zm): if p_zf_i*p_zm_i < trim_lvl: continue for ipsi, p_psi_i in enumerate(p_psi): p = p_zf_i*p_zm_i*p_psi_i if p > trim_lvl: p_vec[ind_conv(izf,izm,ipsi)] = p assert np.any(p_vec>trim_lvl), 'Everything is zero?' p_vec = p_vec / np.sum(p_vec) p_mat[:,iz] = p_vec return p_mat.T
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()
def __init__(self, Mlist, pswitchlist=None, female=True, N=15000, T=30, verbose=True, nosim=False, fix_seed=True): if fix_seed: np.random.seed(18) # 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['Tsim'] 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.female = female self.single_state = 'Female, single' if female else 'Male, single' # all the randomness is here self._shocks_single_iexo = np.random.random_sample((N, T)) self._shocks_single_meet = np.random.random_sample((N, T)) self._shocks_couple_iexo = np.random.random_sample((N, T)) self._shocks_single_a = np.random.random_sample((N, T)) self._shocks_couple_a = np.random.random_sample((N, T)) if female: inc_prob = int_prob(self.setup.exogrid.zf_t[0], sig=self.setup.pars['sig_zf_0']) else: inc_prob = int_prob(self.setup.exogrid.zm_t[0], sig=self.setup.pars['sig_zm_0']) _shocks_init = np.random.random_sample((N, )) i_inc = np.sum((_shocks_init[:, None] > np.cumsum(inc_prob)[None, :]), axis=1) iexoinit = i_inc # initial state self._shocks_outsm = np.random.random_sample((N, T)) self._shocks_transition = np.random.random_sample((N, T)) self._shocks_single_preg = np.random.random_sample((N, T)) self._shocks_planned_preg = np.random.random_sample((N, T)) self._shocks_child_support_fem = np.random.random_sample((N, T)) self._shocks_child_support_mal = np.random.random_sample((N, T)) # no randomnes past this line please # initialize assets self.iassets = np.zeros((N, T), np.int32) # initialize FLS #self.ils=np.ones((N,T),np.float64) self.ils_i = np.zeros((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.c = np.zeros((N, T), np.float32) self.x = np.zeros((N, T), np.float32) self.s = np.zeros((N, T), np.float32) self.unplanned_preg = np.zeros((N, T), dtype=np.bool) self.planned_preg = np.zeros((N, T), dtype=np.bool) self.disagreed = np.zeros((N, T), dtype=np.bool) self.aborted = np.zeros((N, T), dtype=np.bool) self.met_a_partner = np.zeros((N, T), dtype=np.bool) self.agreed = np.zeros((N, T), dtype=np.bool) self.agreed_k = np.zeros((N, T), dtype=np.bool) self.agreed_unplanned = np.zeros((N, T), dtype=np.bool) self.renegotiated = np.zeros((N, T), dtype=np.bool) self.just_divorced = np.zeros((N, T), dtype=np.bool) self.just_divorced_nk = np.zeros((N, T), dtype=np.bool) self.just_divorced_k = np.zeros((N, T), dtype=np.bool) self.new_child = np.zeros((N, T), dtype=np.bool) self.k_m = np.zeros((N, T), dtype=np.bool) self.k_m_true = np.zeros((N, T), dtype=np.bool) self.m_k = np.zeros((N, T), dtype=np.bool) self.nmar = np.zeros((N, T), dtype=np.int8) self.ub_hit_single = False self.ub_hit_couple = False self.yaftmar = -np.ones((N, T), dtype=np.int8) self.iexo[:, 0] = iexoinit # initialize state self.state_codes = dict() self.has_theta = list() self.has_fls = list() for i, name in enumerate(self.setup.state_names): self.state_codes[name] = i self.has_theta.append((name == 'Couple, no children' or name == 'Couple and child')) self.has_fls.append( (name == 'Couple, no children' or name == 'Couple and child' or name == 'Female and child')) 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 = 0 #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() self.compute_aux() counts, offers, marriages = self.marriage_stats() if self.verbose and self.ub_hit_single: print('Assets upped bound is reached for singles') if self.verbose and self.ub_hit_couple: print('Assets upped bound is reached for couples')