def mom_symbol(self, moments): """ Symbolic solver for ordinary method of moments find k-point representative distribution Args: moments: a sequence of estimated moments, starting from degree 1 only use first 2k-1 moments num_comps: number of atoms in the output RV Returns: U(finiteRV): estimated RV, whose first 2k-1 moments equal m """ num = 2 * self.k - 1 assert len(moments) >= num p_var = sympy.symbols('p0:%d' % (self.k), nonnegative=True, real=True) x_var = sympy.symbols('x0:%d' % (self.k), real=True) eqn = -1 for i in range(self.k): eqn = eqn + p_var[i] equations = [eqn] for i in range(num): eqn = -moments[i] ########### truncate a real number to rational # eq = -nsimplify(m[i], tolerance=0.1) # eq = float('%.2g' % -m[i]) for j in range(self.k): eqn = eqn + p_var[j] * (x_var[j]**(i + 1)) equations.append(eqn) var = [x for xs in zip(p_var, x_var) for x in xs] # format: [p1,x1,p2,x2,...] s_var = sympy.solve(equations, var) # s = solveset(equations,list(p)+list(x)) # s = nonlinsolve(equations,list(p)+list(x)) ########## some other tools in SymPy # print(equations) # s = solve_poly_system(equations,list(p)+list(x)) # s = solve_triangulated(equations,list(p)+list(x)) # s = solve_triangulated(equations,p[0],p[1],x[0],x[1]) ########## test over simple rational coefficients # testEq = [x[0]*p[0] - 2*p[0], 2*p[0]**2 - x[0]**2] # s = solve_triangulated(testEq, x[0], p[0]) if len(s_var) == 0: return DiscreteRV(w=[], x=[]) else: return DiscreteRV(w=s_var[0][0::2], x=s_var[0][1::2])
def mom_numeric(self, moments, start): """ numerical solver for ordinary method of moments find k-point representative distribution Args: moments: a sequence of estimated moments, starting from degree 1 only use first 2k-1 moments start: initial guess. k = the number of components in start. Returns: U(finiteRV): estimated RV """ k = len(start.p) assert k == self.k num = 2 * k - 1 assert len(moments) >= num moments = moments[0:num] def equation(x_var): """ local moments condition equation """ model = DiscreteRV(x=x_var[0:k], w=np.append(x_var[k:], 1 - np.sum(x_var[k:]))) return model.moment(num) - moments x0_var = np.concatenate((start.x, start.p[0:-1])) x_var = scipy.optimize.fsolve(equation, x0_var) # x = scipy.optimize.broyden2(equation, x0) return DiscreteRV(x=x_var[0:k], w=np.append(x_var[k:], 1 - np.sum(x_var[k:])))
def equation(x_var): """ local moments condition equation """ model = DiscreteRV(x=x_var[0:k], w=np.append(x_var[k:], 1 - np.sum(x_var[k:]))) return model.moment(num) - moments
def mc_sample_path(P, init=0, sample_size=1000): # === set up array to store output === # X = np.empty(sample_size, dtype=int) if isinstance(init, int): X[0] = init else: X[0] = DiscreteRV(init).draw() # === turn each row into a distribution === # # In particular, let P_dist[i] be the distribution corresponding to the # i-th row P[i,:] n = len(P) P_dist = [DiscreteRV(P[i, :]) for i in range(n)] # === generate the sample path === # for t in range(sample_size - 1): X[t+1] = P_dist[X[t]].draw() return X
def quadmom(moments, dettol=0, inf=1e10): """ compute quadrature from moments ref: Gene Golub, John Welsch, Calculation of Gaussian Quadrature Rules Args: m: moments sequence dettol: tolerant of singularity of moments sequence (quantified by determinant of moment matrix) INF: infinity Returns: U: quadrature """ moments = np.asarray(moments) # INF = float('inf') inf = 1e10 if len(moments) % 2 == 1: moments = np.append(moments, inf) num = int(len(moments) / 2) moments = np.insert(moments, 0, 1) h_mat = hankel(moments[:num + 1:], moments[num::]) # Hankel matrix for i in range(len(h_mat)): # check positive definite and decide to use how many moments if np.linalg.det( h_mat[0:i + 1, 0:i + 1]) <= dettol: # alternative: less than some threshold h_mat = h_mat[0:i + 1, 0:i + 1] h_mat[i, i] = inf num = i break r_mat = np.transpose( np.linalg.cholesky(h_mat)) # upper triangular Cholesky factor # Compute alpha and beta from r, using Golub and Welsch's formula. alpha = np.zeros(num) alpha[0] = r_mat[0][1] / r_mat[0][0] for i in range(1, num): alpha[i] = r_mat[i][i + 1] / r_mat[i][i] - r_mat[i - 1][i] / r_mat[ i - 1][i - 1] beta = np.zeros(num - 1) for i in range(num - 1): beta[i] = r_mat[i + 1][i + 1] / r_mat[i][i] jacobi = np.diag(alpha, 0) + np.diag(beta, 1) + np.diag(beta, -1) eigval, eigvec = np.linalg.eig(jacobi) atoms = eigval weights = moments[0] * np.power(eigvec[0], 2) return DiscreteRV(w=weights, x=atoms)
def mc_sample_path(P, init=0, sample_size=1000): """ Generates one sample path from a finite Markov chain with (n x n) Markov matrix P on state space S = {0,...,n-1}. Parameters ========== P : A nonnegative 2D NumPy array with rows that sum to 1 init : Either an integer in S or a nonnegative array of length n with elements that sum to 1 sample_size : int If init is an integer, the integer is treated as the determinstic initial condition. If init is a distribution on S, then X_0 is drawn from this distribution. Returns ======== A NumPy array containing the sample path """ # === set up array to store output === # X = np.empty(sample_size, dtype=int) if isinstance(init, int): X[0] = init else: X[0] = DiscreteRV(init).draw() # === turn each row into a distribution === # # In particular, let P_dist[i] be the distribution corresponding to the # i-th row P[i,:] n = len(P) P_dist = [DiscreteRV(P[i,:]) for i in range(n)] # === generate the sample path === # for t in range(sample_size - 1): X[t+1] = P_dist[X[t]].draw() return X
def std_rv(self): """ discrete rv for the sigmas """ return DiscreteRV(self.weights, self.sigma)
def mean_rv(self): """ discrete rv for the means """ return DiscreteRV(self.weights, self.centers)
def __init__(self, agents, D, D_probs, aggregate_asset_endowment=1.0, seed=None, lucas_tree=False): ''' Market simply consists of collection of agents. lucas_tree: True means agents get same allocation of xi0 every morning ''' self.seed = seed self.RNG = np.random.RandomState(self.seed) self.lucas_tree = lucas_tree self.agents = agents self.price_history = [] self.volume_history = [] self.payoff_history = [] self.bilateral_price_history = [] self.bilateral_volume_history = [] self.bilateral_trade_partner_history = [] self.total_agent_cash_wealth_history = [] self.total_agent_initial_asset_endowment = [] #self.total_agent_asset_volume = [] self.aggregate_asset_endowment = aggregate_asset_endowment # Set up storage for agent values: self.agents_history = [] # Store agent variables here. for i in range(len(self.agents)): temp_storage = {'y': [], 'c': [], 'xi': []} self.agents_history.append(temp_storage) # To find reasonable "first guess" price, find the risk-neutral asset # asset price for first-agent's preferences: agent0 = self.agents[0] self.p0_guess = agent0.beta * np.dot(agent0.D, agent0.D_probs) if self.p0_guess <= 0.0: self.p0_guess = 1e-5 self.p_tm1 = self.p0_guess # Set up the dividend process: # Ensure that dividend payoffs are sorted from smallest to largest: self.D = np.array(D) self.D_probs = np.array(D_probs) ii = np.argsort(self.D) self.D = self.D[ii] self.D_probs = self.D_probs[ii] # Quick check: assert np.isclose( np.sum(D_probs), 1.0 ), "Problem: p_dividend does not sum to 1.0: np.sum(D_probs) = " + str( np.sum(D_probs)) D_seed = self.RNG.randint(low=0, high=(2**31) - 1) self.D_process = DiscreteRV(self.D_probs, self.D, seed=D_seed)