def make_positive_definite(m, tol=None): ''' Computes a matrix close to the original matrix m that is positive definite. This function is just a transcript of R' make.positive.definite function. m (2d array): A matrix that is not necessary psd. tol (int): A tolerence level controlling how "different" the psd matrice can be from the original matrix --------------------------------------------------------------- returns (2d array): A psd matrix ''' d = m.shape[0] if (m.shape[1] != d): raise RuntimeError("Input matrix is not square!") eigvalues, eigvect = eigh(m) # Sort the eigen values idx = eigvalues.argsort()[::-1] eigvalues = eigvalues[idx] eigvect = eigvect[:, idx] if (tol == None): tol = d * np.max(np.abs(eigvalues)) * sys.float_info.epsilon delta = 2 * tol tau = np.maximum(0, delta - eigvalues) dm = multi_dot([eigvect, np.diag(tau), eigvect.T]) return (m + dm)
def plot_single(result, x_opt, f_opt, method): # Unpack result t_hist = result['t_hist'] x_hist = result['x_hist'] f_hist = result['f_hist'] g_hist = result['g_hist'] h_hist = result['h_hist'] # Plot fig, ax = plt.subplots(nrows=4, sharex=True, figsize=(6, 10)) ax[0].semilogy(t_hist, la.norm(x_hist - x_opt, axis=1)) ax[0].set_ylabel('Iterate error norm') ax[1].semilogy(t_hist, f_hist - f_opt) ax[1].set_ylabel('Objective error') ax[2].semilogy(t_hist, la.norm(g_hist, axis=1)) ax[3].axhline(MIN_GRAD_NORM, linestyle='--', color='k', alpha=0.5) ax[2].set_ylabel('Gradient norm') ax[3].plot(t_hist, np.array([la.eigh(h)[0] for h in h_hist])) ax[3].axhline(0, linestyle='--', color='k', alpha=0.5) ax[3].set_yscale('symlog') ax[3].set_ylabel('Hessian eigenvalues') ax[-1].set_xlabel('Iteration') ax[0].set_title(method) return fig, ax
def solve_sylvester(a, b, q): if a.shape == b.shape: axes = (0, 2, 1) if a.ndim == 3 else (1, 0) if np.all(a == b) and np.all(np.abs(a - np.transpose(a, axes)) < 1e-12): eigvals, eigvecs = eigh(a) if np.all(eigvals >= 1e-12): tilde_q = np.transpose(eigvecs, axes) @ q @ eigvecs tilde_x = tilde_q / (eigvals[..., :, None] + eigvals[..., None, :]) return eigvecs @ tilde_x @ np.transpose(eigvecs, axes) return np.vectorize( scipy.linalg.solve_sylvester, signature="(m,m),(n,n),(m,n)->(m,n)" )(a, b, q)
def diagonal_cond(H_old, psi_old): ''' Ensure that Lambda^T Psi^{-1} Lambda is diagonal H_old (list of nb_layers elements of shape (K_l x r_l-1, r_l)): The previous iteration values of Lambda estimators psi_old (list of ndarrays): The previous iteration values of Psi estimators (list of nb_layers elements of shape (K_l x r_l-1, r_l-1)) ------------------------------------------------------------------------ returns (list of ndarrays): An "identifiable" H estimator (2nd condition) ''' L = len(H_old) H = [] for l in range(L): B = np.transpose(H_old[l], (0, 2, 1)) @ pinv(psi_old[l]) @ H_old[l] values, vec = eigh(B) H.append(H_old[l] @ vec) return H
def posdefify(A, eps): E, V = la.eigh(A) return np.dot(V, np.dot(np.diag(np.maximum(E, eps)), V.T))