def _parse_shocks(optim_paras, params): """Parse the shock parameters and create the Cholesky factor.""" if sum(f"shocks_{i}" in params.index for i in ["sdcorr", "cov", "chol"]) >= 2: raise ValueError("It is not allowed to define multiple shock matrices.") elif "shocks_sdcorr" in params.index: sorted_shocks = _sort_shocks_sdcorr(optim_paras, params.loc["shocks_sdcorr"]) cov = sdcorr_params_to_matrix(sorted_shocks) optim_paras["shocks_cholesky"] = robust_cholesky(cov) elif "shocks_cov" in params.index: sorted_shocks = _sort_shocks_cov_chol( optim_paras, params.loc["shocks_cov"], "cov" ) cov = cov_params_to_matrix(sorted_shocks) optim_paras["shocks_cholesky"] = robust_cholesky(cov) elif "shocks_chol" in params.index: sorted_shocks = _sort_shocks_cov_chol( optim_paras, params.loc["shocks_chol"], "chol" ) optim_paras["shocks_cholesky"] = chol_params_to_lower_triangular_matrix( sorted_shocks ) else: raise NotImplementedError return optim_paras
def covariance_to_internal_jacobian(external_values, constr): r"""Jacobian of ``covariance_to_internal``. For reference see docstring of ``jacobian_covariance_from_internal``. In comparison to that function, however, here we want to differentiate the reverse graph external --> cov --> cholesky --> internal Again use the vectors :math:`c` and :math:`x` to denote the external and internal values, respectively. To solve for the jacobian we make use of the identity .. math:: \frac{\mathrm{d}x}{\mathrm{d}c} = (\frac{\mathrm{d}c}{\mathrm{d}x})^{-1} Args: external_values (np.ndarray): Row-wise half-vectorized covariance matrix Returns: deriv: The Jacobian matrix. """ cov = cov_params_to_matrix(external_values) chol = robust_cholesky(cov) internal = chol[np.tril_indices(len(chol))] deriv = covariance_from_internal_jacobian(internal, constr=None) deriv = np.linalg.pinv(deriv) return deriv
def _parse_shocks(optim_paras, params): """Parse the shock parameters and create the Cholesky factor.""" if sum(f"shocks_{i}" in params.index for i in ["sdcorr", "cov", "chol"]) >= 2: raise ValueError("It is not allowed to define multiple shock matrices.") elif "shocks_sdcorr" in params.index: cov = sdcorr_params_to_matrix(params.loc["shocks_sdcorr"]) optim_paras["shocks_cholesky"] = robust_cholesky(cov) elif "shocks_cov" in params.index: cov = cov_params_to_matrix(params.loc["shocks_cov"]) optim_paras["shocks_cholesky"] = robust_cholesky(cov) elif "shocks_chol" in params.index: optim_paras["shocks_cholesky"] = chol_params_to_lower_triangular_matrix( params.loc["shocks_chol"] ) else: raise KeyError("No shock matrix is specified.") return optim_paras
def update_cholcov(shocks_cholesky, n_wages): """Calculate cholesky factors of conditional covs for all possible cases. Parameters ---------- shocks_cholesky : numpy.ndarray cholesky factor of the covariance matrix before updating. Has dimension (n_choices, n_choices) n_wages : int Number of wage sectors. Returns ------- updated_chols : numpy.ndarray Array of (shape n_wages + 1, n_choices, n_choices) with the cholesky factors of the updated covariance matrices for each possible observed shock. The last element corresponds to not observing any shock. """ n_choices = len(shocks_cholesky) cov = shocks_cholesky @ shocks_cholesky.T updated_chols = np.zeros((n_wages + 1, n_choices, n_choices)) for i in range(n_wages): reduced_cov = np.delete(np.delete(cov, i, axis=1), i, axis=0) choice_var = cov[i, i] f = np.delete(cov[i], i) updated_reduced_cov = reduced_cov - np.outer(f, f) / choice_var updated_reduced_chol = robust_cholesky(updated_reduced_cov) updated_chols[i, :i, :i] = updated_reduced_chol[:i, :i] updated_chols[i, :i, i + 1:] = updated_reduced_chol[:i, i:] updated_chols[i, i + 1:, :i] = updated_reduced_chol[i:, :i] updated_chols[i, i + 1:, i + 1:] = updated_reduced_chol[i:, i:] updated_chols[-1] = shocks_cholesky return updated_chols
cov = sdcorr_params_to_matrix(params_subset["value"].to_numpy()) else: raise ValueError("Invalid type_: {}".format(type_)) dim = len(cov) e, v = np.linalg.eigh(cov) assert np.all(e > -1e-8), "Invalid covariance matrix." if case == "uncorrelated": res["lower"] = np.maximum(res["lower"], np.zeros(len(res))) assert (res["upper"] >= res["lower"]).all(), "Invalid upper bound for variance." elif case == "free": chol = robust_cholesky(cov, threshold=1e-8) chol_coeffs = chol[np.tril_indices(dim)] res["value"] = chol_coeffs lower_bound_helper = np.full((dim, dim), -np.inf) lower_bound_helper[np.diag_indices(dim)] = bounds_distance res["lower"] = lower_bound_helper[np.tril_indices(dim)] res["upper"] = np.inf for bound in ["lower", "upper"]: if np.isfinite(params_subset[bound]).any(): warnings.warn("Bounds are ignored for covariance parameters.", UserWarning) return res
def test_robust_cholesky_with_extreme_cases(): for cov in [np.ones((5, 5)), np.zeros((5, 5))]: chol = robust_cholesky(cov) aaae(chol.dot(chol.T), cov)
def test_robust_cholesky_with_zero_variance(dim, seed): cov = random_cov(dim, seed) chol = robust_cholesky(cov) aaae(chol.dot(chol.T), cov) assert (chol[np.triu_indices(len(cov), k=1)] == 0).all()
def covariance_to_internal(external_values, constr): """Do a cholesky reparametrization.""" cov = cov_params_to_matrix(external_values) chol = robust_cholesky(cov) return chol[np.tril_indices(len(cov))]
def sdcorr_to_internal(external_values, constr): """Convert sdcorr to cov and do a cholesky reparametrization.""" cov = sdcorr_params_to_matrix(external_values) chol = robust_cholesky(cov) return chol[np.tril_indices(len(cov))]