def _get_derivatives_implied_cov(self): """ Compute the derivatives for the implied covariance matrix (sigma). Returns ------- loadings_dx : numpy array The derivative of the loadings matrix. factor_covs_dx : numpy array The derivative of the factor covariance matrix. error_covs_dx : numpy array The derivative of the error covariance matrix. """ # meets all of our expected criteria check_is_fitted(self, 'loadings_') loadings = self.loadings_.copy() factor_covs = self.factor_varcovs_.copy() sym_lower_var_idx = get_symmetric_lower_idxs(self.model.n_variables) sym_upper_fac_idx = get_symmetric_upper_idxs(self.model.n_factors, diag=False) sym_lower_fac_idx = get_symmetric_lower_idxs(self.model.n_factors, diag=False) factors_diag = np.eye(self.model.n_factors) factors_diag_mult = factors_diag.dot(factor_covs).dot(factors_diag.T).dot(loadings.T) # calculate the derivative of the loadings matrix, using the commutation matrix loadings_dx = np.eye(self.model.n_variables**2) + commutation_matrix(self.model.n_variables, self.model.n_variables) loadings_dx = loadings_dx.dot(np.kron(factors_diag_mult, np.eye(self.model.n_variables)).T) # calculate the derivative of the factor_covs matrix factor_covs_dx = loadings.dot(factors_diag) factor_covs_dx = np.kron(factor_covs_dx, factor_covs_dx) off_diag = (factor_covs_dx[:, sym_lower_fac_idx] + factor_covs_dx[:, sym_upper_fac_idx]) combine_indices = np.concatenate([sym_upper_fac_idx, sym_lower_fac_idx]) combine_diag = np.concatenate([off_diag, off_diag], axis=1) factor_covs_dx[:, combine_indices] = combine_diag factor_covs_dx = factor_covs_dx[:, :factor_covs.size] # calculate the derivative of the error_cov matrix, # which we assume will always be a diagonal matrix error_covs_dx = np.eye(self.model.n_variables**2) # make sure these matrices are symmetric loadings_dx = loadings_dx[sym_lower_var_idx, :] factor_covs_dx = factor_covs_dx[sym_lower_var_idx, :] error_covs_dx = error_covs_dx[sym_lower_var_idx, :] # we also want to calculate the derivative for the intercepts intercept_dx = np.zeros((loadings_dx.shape[0], self.model.n_variables), dtype=float) return (loadings_dx[:, self.model.loadings_free].copy(), factor_covs_dx[:, self.model.factor_covs_free].copy(), error_covs_dx[:, self.model.error_vars_free].copy(), intercept_dx)
def __init__(self, loadings, n_factors, n_variables, factor_names=None, variable_names=None): assert isinstance(loadings, np.ndarray) assert loadings.shape[0] == n_variables assert loadings.shape[1] == n_factors self._loadings = loadings self._n_factors = n_factors self._n_variables = n_variables self._factor_names = factor_names self._variable_names = variable_names self._n_lower_diag = get_symmetric_lower_idxs(n_factors, False).shape[0] self._error_vars = np.full((n_variables, 1), np.nan) self._factor_covs = np.full((n_factors, n_factors), np.nan) self._loadings_free = get_free_parameter_idxs(loadings, eq=1) self._error_vars_free = merge_variance_covariance(self._error_vars) self._error_vars_free = get_free_parameter_idxs(self._error_vars_free, eq=-1) self._factor_covs_free = get_symmetric_lower_idxs(n_factors, False)
def test_get_symmetric_lower_idxs_no_diag(): expected = np.array([5, 10, 11, 15, 16, 17, 20, 21, 22, 23]) output = get_symmetric_lower_idxs(5, diag=False) assert_array_equal(output, expected)
def test_get_symmetric_lower_idxs(): expected = np.array([0, 1, 2, 3, 4, 6, 7, 8, 9, 12, 13, 14, 18, 19, 24]) output = get_symmetric_lower_idxs(5) assert_array_equal(output, expected)