def partial_svd(transform, r=1, extra_rank=2, max_its = 1000, tol = 1e-8, initial=None, return_full = False, debug=False): """ Compute the partial SVD of the linear_transform X using the Mazumder/Hastie algorithm in (TODO: CITE) """ if isinstance(transform, np.ndarray): transform = linear_transform(transform) n = transform.dual_shape[0] p = transform.primal_shape[0] r = np.int(np.min([r,p])) q = np.min([r + extra_rank, p]) if initial is not None: if initial.shape == (n,q): U = initial elif len(initial.shape) == 1: U = np.hstack([initial.reshape((initial.shape[0],1)), np.random.standard_normal((n,q-1))]) else: U = np.hstack([initial, np.random.standard_normal((n,q-initial.shape[1]))]) else: U = np.random.standard_normal((n,q)) if return_full: ind = np.arange(q) else: ind = np.arange(r) old_singular_values = np.zeros(r) change_ind = np.arange(r) itercount = 0 singular_rel_change = 1. while itercount < max_its and singular_rel_change > tol: if debug and itercount > 0: print itercount, singular_rel_change, np.sum(np.fabs(singular_values)>1e-12), np.fabs(singular_values[range(np.min([5,len(singular_values)]))]) V,_ = np.linalg.qr(transform.adjoint_map(U)) X_V = transform.linear_map(V) U,R = np.linalg.qr(X_V) singular_values = np.diagonal(R)[change_ind] singular_rel_change = np.linalg.norm(singular_values - old_singular_values)/np.linalg.norm(singular_values) old_singular_values = singular_values * 1. itercount += 1 singular_values = np.diagonal(R)[ind] nonzero = np.where(np.fabs(singular_values) > 1e-12)[0] if len(nonzero): return U[:,ind[nonzero]] * np.sign(singular_values[nonzero]), np.fabs(singular_values[nonzero]), V[:,ind[nonzero]].T else: return U[:,ind[0]], np.zeros((1,1)), V[:,ind[0]].T
def _setX(self,transform): if isinstance(transform, np.ndarray): transform = linear_transform(transform) self.primal_shape = transform.primal_shape self.dual_shape = transform.dual_shape U, D, VT = compute_iterative_svd(transform, min_singular=self.min_singular, tol=self.tol, initial_rank = self.initial_rank, initial=self.initial, debug=self.debug) self.SVD = [U,D,VT]
def compute_iterative_svd(transform, initial_rank = None, initial = None, min_singular = 1e-16, tol = 1e-5, debug=False): """ Compute the SVD of a matrix using partial_svd """ if isinstance(transform, np.ndarray): transform = linear_transform(transform) n = transform.dual_shape[0] p = transform.primal_shape[0] if initial_rank is None: r = np.round(np.min([n,p]) * 0.1) + 1 else: r = np.max([initial_rank,1]) min_so_far = 1. D = [np.inf] while D[-1] >= min_singular: if debug: print "Trying rank", r U, D, VT = partial_svd(transform, r=r, extra_rank=5, tol=tol, initial=initial, return_full=True, debug=debug) if D[0] < min_singular: return U[:,0], np.zeros((1,1)), VT[0,:] if len(D) < r: break initial = 1. * U r *= 2 ind = np.where(D >= min_singular)[0] return U[:,ind], D[ind], VT[ind,:]