def twist_scenarios_mom_match(x, m_, s2_, p=None, method='Riccati', d=None): """For details, see here. Parameters ---------- x : array, shape (j_,n_) if n_>1 or (j_,) for n_=1 m_ : array, shape (n_,) s2_ : array, shape (n_,n_) p : array, optional, shape (j_,) method : string, optional d : array, shape (k_, n_), optional Returns ------- x : array, shape (j_, n_) if n_>1 or (j_,) for n_=1 """ if np.ndim(m_) == 0: m_ = np.reshape(m_, 1).copy() else: m_ = np.array(m_).copy() if np.ndim(s2_) == 0: s2_ = np.reshape(s2_, (1, 1)) else: s2_ = np.array(s2_).copy() if len(x.shape) == 1: x = x.reshape(-1, 1).copy() if p is None: j_ = x.shape[0] p = np.ones(j_) / j_ # uniform probabilities as default value # Step 1. Original moments m_x, s2_x = meancov_sp(x, p) # Step 2. Transpose-square-root of s2_x r_x = transpose_square_root(s2_x, method, d) # Step 3. Transpose-square-root of s2_ r_ = transpose_square_root(s2_, method, d) # Step 4. Twist matrix b = r_ @ np.linalg.inv(r_x) # Step 5. Shift vector a = m_.reshape(-1, 1) - b @ m_x.reshape(-1, 1) # Step 6. Twisted scenarios x_ = (a + b @ x.T).T return np.squeeze(x_)
def simulate_t(mu, sigma2, nu, j_, stoc_rep=None, method='Riccati', d=None): """For details, see here. Parameters ---------- mu : array, shape (n_,) sigma2 : array, shape (n_,n_) nu : int j_ : int stoc_rep : bool, optional method : string, optional d : array, shape (k_,n_), optional Returns ------- x : array, shape(j_,n_) if n_>1 or (j_,) for n_=1 """ if isinstance(mu, (list, tuple, np.ndarray)): mu = np.array(mu) sigma2 = np.array(sigma2) n_ = len(mu) else: n_ = 1 mu = np.reshape(mu, n_) sigma2 = np.reshape(sigma2, (n_, n_)) if stoc_rep is None: stoc_rep = False if stoc_rep is False: # Step 1: Riccati root sigma = transpose_square_root(sigma2, method, d) # Step 2: Radial scenarios r = np.sqrt(n_ * sp.stats.f.ppf(np.random.rand(j_, 1), n_, nu)) # Step 3: Normal scenarios n = simulate_normal(np.zeros(n_), np.eye(n_), j_).reshape(-1, n_) # Step 4: Uniform component normalizers = np.linalg.norm(n, axis=1) y = n / normalizers[:, None] # Step 5: Output x = mu + r * y @ sigma else: # Alternative stochastic representation # Step 6: Normal scenarios n = simulate_normal(np.zeros(n_), sigma2, j_, method, d).reshape(-1, n_) # Step 7: Chi-squared scenarios v = sp.stats.chi2.ppf(np.random.rand(j_, 1), nu) # Step 8: Output x = mu + n / np.sqrt(v / nu) return np.squeeze(x)
def multi_r2(s2_u, s2_x, sigma2=None): """For details, see here. Parameters ---------- s2_u : array, shape(n_, n_) s2_x : array, shape(n_, n_) s2: array, shape(n_, n_) Returns ------- r2 : float """ if sigma2 is None: r2 = 1.0 - np.trace(s2_u) / np.trace(s2_x) # r-squared else: sigma_cholesky = transpose_square_root( sigma2, method='Cholesky') # Cholesky root ss_res = np.trace( sp.linalg.solve_triangular( sigma_cholesky, sp.linalg.solve_triangular(sigma_cholesky, s2_u, lower=True).T).T) ss_tot = np.trace( sp.linalg.solve_triangular( sigma_cholesky, sp.linalg.solve_triangular(sigma_cholesky, s2_x, lower=True).T).T) r2 = 1.0 - ss_res / ss_tot # r-squared return r2
def simulate_quadn(alpha, beta, gamma, mu, sigma2, j_): """For details, see here. Parameters ---------- alpha : float beta : array, shape(n_,) gamma : array, shape(n_, n_) mu : array, shape(n_,) sigma2 : array, shape(n_, n_) j_ : float Returns ------- y : array, shape(j_,) p_ : array, shape(j_,) """ if np.ndim(beta) == 1: n_ = len(beta) else: n_ = 1 beta, gamma = np.reshape(beta, (n_, 1)), np.reshape(gamma, (n_, n_)) mu, sigma2 = np.reshape(mu, (n_, 1)), np.reshape(sigma2, (n_, n_)) # Step 1: Perform cholesky decomposition of sigma2 l = transpose_square_root(sigma2, 'Cholesky') # Step 2: Compute parameter lambda lambd, e = np.linalg.eig(l.T @ gamma @ l) lambd = lambd.reshape(-1, 1) # Step 3: Compute new parameters beta_tilde and gamma_tilde beta_tilde = beta + 2 * gamma @ mu gamma_tilde = e.T @ l.T @ beta_tilde # Step 4: Generate Monte Carlo scenarios z = simulate_normal(np.zeros(n_), np.eye(n_), j_).reshape(-1, n_) y = alpha + beta.T @ mu + mu.T @ gamma @ mu + \ z @ gamma_tilde + z ** 2 @ lambd # Step 5: Match expectation and variance by twisting probabilities e_y = alpha + beta.T @ mu + mu.T @ gamma @ mu + np.trace(sigma2 @ gamma) v_y = 2 * np.trace(np.linalg.matrix_power(gamma @ sigma2, 2)) + \ beta_tilde.T @ sigma2 @ beta_tilde p_ = twist_prob_mom_match(y.reshape(-1, 1), e_y[0], v_y).reshape(-1) return np.squeeze(y), np.squeeze(p_)
def simulate_unif_in_ellips(mu, sigma2, j_): """For details, see here. Parameters ---------- mu : array, shape (n_,) sigma2 : array, shape (n_,n_) j_ : int Returns ------- x : array, shape(j_,n_) r : array, shape (j_,) y : array, shape (j,n_) """ n_ = len(mu) # Step 1. Riccati root sigma = transpose_square_root(sigma2) # Step 2. Radial scenarios r = (np.random.rand(j_, 1))**(1 / n_) # Step 3. Normal scenarios n = simulate_normal(np.zeros(n_), np.eye(n_), j_).reshape(-1, n_) # Step 4. Uniform component normalizers = np.linalg.norm(n, axis=1) y = n / normalizers[:, None] # Step 5. Output x = mu + r * y @ sigma return x, r, y
def saddle_point_quadn(y, alpha, beta, gamma, mu, sigma2): """For details, see here. Parameters ---------- y : array, shape(j_,) alpha : scalar beta : array, shape(n_,) gamma : array, shape(n_, n_) mu : array, shape(n_,) sigma2 : array, shape(n_, n_) Returns ------- cdf : array, shape(j_,) pdf : array, shape(j_,) """ y = np.asarray(y).copy().reshape(-1) beta = np.asarray(beta).copy().reshape(-1, 1) mu = np.asarray(mu).copy().reshape(-1, 1) j_ = len(y) # Step 1: Compute the eigenvalues and eigenvectors of l.T @ gamma @ l l = transpose_square_root(sigma2, 'Cholesky') lam, e = np.linalg.eig(l.T @ gamma @ l) lam = lam.reshape(-1, 1) # Step 2: Compute transformed parameters alpha_tilde = alpha + beta.T @ mu + mu.T @ gamma @ mu beta_tilde = beta + 2*gamma @ mu gamma_tilde = e.T @ l.T @ beta_tilde # Step 3: Compute the log-characteristic function and its derivatives # log-characteristic function def c_y(w): return alpha_tilde * w - 0.5 * np.sum(np.log(1 - 2.*w*lam) - w**2 * gamma_tilde**2 / (1 - 2.*w*lam)) # first derivative def c_y_prime(w): return alpha_tilde + np.sum(lam / (1 - 2.*w*lam) + gamma_tilde**2 * (w - w**2 * lam) / (1 - 2.*w*lam)**2) # second derivative def c_y_second(w): return np.array([np.sum(2. * (lam / (1 - 2.*w*lam))**2 + gamma_tilde**2 / (1 - 2.*w*lam)**3)]) # Step 4: Find w_hat numerically using Brent's method lam_max = np.max(lam) lam_min = np.min(lam) if lam_max > 0: w_max = (1 - 1e-5) / (2 * lam_max) else: w_max = 1e20 if lam_min < 0: w_min = (1 + 1e-5) / (2 * lam_min) else: w_min = -1e20 y_min = c_y_prime(w_min) y_max = c_y_prime(w_max) # initialize w_hat = np.zeros(j_) c_y_w_hat = np.zeros(j_) # c(w_hat) c_y_second_w_hat = np.zeros(j_) # c''(w_hat) idx = np.argsort(y) w_last = w_min for j in range(j_): if y[idx[j]] <= y_min: w_hat[idx[j]] = w_min elif y[idx[j]] >= y_max: w_hat[idx[j]] = w_max else: # Brent’s method for finding the root of the function. # Since y is sorted and c_y_prime is a monotone increasing function # it is guaranteed that the solution w is in the interval # [w_last, w_max]. w_hat[idx[j]] = brentq(lambda w: c_y_prime(w) - y[idx[j]], w_last, w_max) w_last = w_hat[idx[j]] c_y_w_hat[idx[j]] = c_y(w_hat[idx[j]]) c_y_second_w_hat[idx[j]] = c_y_second(w_hat[idx[j]]) # Step 5: Compute cdf and pdf r = np.sign(w_hat) * np.sqrt(2. * (w_hat * y - c_y_w_hat)) u = w_hat * np.sqrt(c_y_second_w_hat) cdf = norm.cdf(r) - norm.pdf(r) * (1. / u - 1. / r) pdf = np.exp(c_y_w_hat - w_hat * y) / np.sqrt(2 * np.pi * c_y_second_w_hat) return np.squeeze(cdf), np.squeeze(pdf)