def _diagonal_operator(diag): """Creates an operator representing a multiplication with a diagonal matrix""" diag = diag.ravel()[:, np.newaxis] def diag_matvec(vec): if vec.ndim > 1: return diag * vec else: return diag.ravel() * vec linop = LinearOperator(shape=(len(diag), len(diag)), matvec=diag_matvec, rmatvec=diag_matvec, dtype=np.float64) linop.matvec = diag_matvec linop.rmatvec = diag_matvec return linop
def get_grad_linop(X, Y, invcovB, invcovN, alpha): """ Linear operator implementing the gradient of the functional \frac{1}{2} \|Y - XB\|^2_{\Sigma_n} + \frac{1}{2} \|B\|^2_{\Sigma_s} which reads grad_B = X^T(XB - Y)\Sigma_n^{-1} + \lambda B\Sigma_s^{-1} """ N, P = X.shape T = invcovB.shape[0] if P <= N: XTX = aslinearoperator(X.T.dot(X)) XTYinvcovN = invcovN.rmatvec(Y.T.dot(X)).T def matvec(vecB): XTXB = XTX.matvec(vecB.reshape(T, P).T) XTXB_invcovN = invcovN.rmatvec(XTXB.T).T B_incovB = invcovB.rmatvec(vecB.reshape(T, P)).T result = XTXB_invcovN - XTYinvcovN + alpha * B_incovB return result.T.ravel() else: # raise(Exception) def matvec(vecB): XB_minus_Y_invcovN = invcovN.rmatvec( (X.dot(vecB.reshape(T, P).T) - Y).T).T XT_XB_minus_Y_invcovN = X.T.dot(XB_minus_Y_invcovN) B_incovB = invcovB.rmatvec(vecB.reshape(T, P)).T result = XT_XB_minus_Y_invcovN + alpha * B_incovB return result.T.ravel() linop = LinearOperator(shape=tuple([X.shape[1] * Y.shape[1]] * 2), matvec=matvec, rmatvec=matvec, dtype=np.dtype('float64')) linop.matvec = matvec linop.rmatvec = matvec linop.dtype = np.dtype('float64') return linop
def _woodbury_inverse(Ainv, Cinv, U, V): """Uses Woodbury Matrix Identity to invert the Matrix (A + UCV) ^ (-1) See http://en.wikipedia.org/wiki/Woodbury_matrix_identity""" def matvec(x): # this is probably wildly suboptimal, but it works Ainv_x = Ainv.matvec(x) Cinv_mat = Cinv.matvec(np.eye(Cinv.shape[0])) VAinvU = V.dot(Ainv.matvec(U)) inv_Cinv_plus_VAinvU = np.linalg.inv(Cinv_mat + VAinvU) VAinv_x = V.dot(Ainv_x) inv_blabla_VAinv_x = inv_Cinv_plus_VAinvU.dot(VAinv_x) whole_big_block = Ainv.matvec( U.dot(inv_blabla_VAinv_x)) return Ainv_x - whole_big_block shape = Ainv.shape linop = LinearOperator(shape=shape, matvec=matvec) linop.matvec = matvec linop.rmatvec = matvec return linop
#!/usr/bin/env python # encoding:utf-8 import numpy as np from scipy.sparse.linalg import LinearOperator def mv(v): print("v:{}".format(v)) print("v:{}".format(v.shape)) return np.array([2 * v[0], 3 * v[1]]) A = LinearOperator((2, 3), matvec=mv) print("A.matvec:{}".format(A.matvec(np.ones(3))))
def calculate_delta(gamma, #3-dim array (Nz,Nx,Ny) of complex shear P_kd, #line-of-sight kappa-delta transform P_gk, #gamma-kappa transform S_dd, #expected signal covariance of delta # (can be arbitrary form) N, #shear noise alpha, #weiner filter strength M_factor = 1 #optional scaling of M before cg-method ): """ Implement equation A3 from Simon & Taylor 2009 Note: their notation Q -> our notation P_kd Also, here we factor N_d^-1 out of M: M should be Hermitian for the cg method. The Simon 09 expression is Hermitian only if N_d is proportional to the identity, which will not be the case for deweighted border pixels. """ P_kd = as_Lens3D_matrix(P_kd) P_gk = as_Lens3D_matrix(P_gk) S_dd = as_Lens3D_matrix(S_dd) N = as_Lens3D_matrix(N) P_gk_cross = P_gk.conj_transpose() P_kd_T = P_kd.transpose() print "calculating delta:" print " shape of P_gk:",P_gk.shape print " shape of P_kd:",P_kd.shape print "constructing linear operator M" #define an operator which performs matrix-vector # multiplication representing M def matvec(v): v0 = P_gk_cross.view_as_Lens3D_vec(v) v1 = N.matvec(v0) v1 *= alpha v2 = P_gk_cross.matvec( v0 ) v2 = P_kd_T.matvec( v2 ) v2 = S_dd.matvec( v2 ) v2 = P_kd.matvec( v2 ) v2 = P_gk.matvec( v2 ) ret = numpy.zeros(v.shape,dtype=complex) ret += v1.vec ret += v2.vec return P_gk_cross.view_as_same_type( ret * M_factor , v ) M = LinearOperator(P_gk.shape, matvec=matvec, dtype=complex) v = numpy.random.random(M.shape[1]) t0 = time() v2 = M.matvec(v) t = time()-t0 print " M multiplication: %.3g sec" % t #print M.matvec(numpy.ones(M.shape[1]))[:10] #exit() print "constructing preconditioner for M" #define an operator which can quickly approximate the inverse of # M using fourier-space inversions. This inverse will be exact for # a noiseless reconstruction on an infinite field P_gk_I = P_gk.inverse(False) #P_kd_I = P_kd.inverse() S_dd_I = S_dd.inverse(False) #P_kd_I_T = P_kd_I.transpose() P_gk_I_cross = P_gk_I.conj_transpose() def matvec_pc(v): v0 = P_gk_I.view_as_Lens3D_vec(v) v0 = P_gk_I.matvec(v0) #v0 = P_kd_I.matvec(v0) v0 = S_dd_I.matvec(v0) #v0 = P_kd_I_T.matvec(v0) v0 = P_gk_I_cross.matvec(v0) return P_gk_I.view_as_same_type(v0,v) M_pc = LinearOperator( (M.shape[1],M.shape[0]), matvec = matvec_pc, dtype = M.dtype ) v = numpy.random.random(M_pc.shape[1]) t0 = time() v3 = M_pc.matvec(v) t_pc = time()-t0 print " preconditioner multiplication: %.3g sec" % t_pc step1_vec = gamma.vec use_cg = True #---define callback function--- def callback(self): callback.N += 1 if callback.N%100 == 0: print callback.N,'iterations' callback.N = 0 #------------------------------ t0 = time() print "calculating cg:" ret,errcode = cg( M, step1_vec, x0 = numpy.zeros(M.shape[1],dtype=step1_vec.dtype), callback=callback, M = M_pc) if errcode != 0: raise ValueError, "calculate_delta: cg iterations did not converge: err = %s" % (str(errcode)) tf = time() print " cg: total time = %.2g sec" % (tf-t0) print " number of iterations = %i" % callback.N print " time per iteration = %.2g sec" % ( (tf-t0)/callback.N ) ret *= M_factor ret = P_gk_cross * ret ret = P_kd_T * ret delta = S_dd * ret return P_kd.view_as_Lens3D_vec(delta)
def estimate_condition_number(P_kd, P_gk, S_dd, N, alpha, compute_exact = False): P_kd = as_Lens3D_matrix(P_kd) P_gk = as_Lens3D_matrix(P_gk) S_dd = as_Lens3D_matrix(S_dd) N = as_Lens3D_matrix(N) P_gk_cross = P_gk.conj_transpose() P_kd_T = P_kd.transpose() def matvec(v): v0 = P_gk_cross.view_as_Lens3D_vec(v) v2 = P_gk_cross.matvec( v0 ) v2 = P_kd_T.matvec( v2 ) v2 = S_dd.matvec( v2 ) v2 = P_kd.matvec( v2 ) v2 = P_gk.matvec( v2 ) v1 = N.matvec(v0) v1 *= alpha ret = numpy.zeros(v.shape,dtype=complex) ret += v1.vec ret += v2.vec return P_gk_cross.view_as_same_type( ret , v ) M = LinearOperator(P_gk.shape, matvec=matvec, dtype=complex) #compute the exact condition number if compute_exact: v = numpy.random.random(M.shape[1]) t0 = time() v2 = M.matvec(v) t = time()-t0 print " - constructing matrix representation (est. %s)" \ % printtime(t*M.shape[0]) t0 = time() M_rep = get_mat_rep(M) print " time to get mat rep: %.2g sec" % (time()-t0) print " - computing SVD" t0 = time() sig = numpy.linalg.svd(M_rep,compute_uv=False) print " time for SVD: %.2g sec" % (time()-t0) print 'true condition number: %.2e / %.2e = %.2e' \ % (sig[0],sig[-1], sig[0]/sig[-1]) #estimate condition number, assuming the noiseless matrix # is rank-deficient. This will be true if there are more # source lens-planes than mass lens-planes eval_max,evec_max = arpack.eigen(M,1) print 'estimated condition number: %.2e / %.2e = %.2e' \ % ( abs(eval_max[0]) , numpy.min(N.data), abs(eval_max[0]) / numpy.min(N.data) )
class ConvolutionMatrix(object): """ Convolution matrix container. """ def __init__(self, A=None, mv=None, rmv=None, shape=None): self.A = A if A is not None: self.shape = A.shape def mv(v): return A @ v def rmv(v): return A.conjugate().T @ v elif any([mv is not None, rmv is not None]): if shape: self.shape = shape else: print('If A is not given, its shape must be provided.') raise Exception(RuntimeError) if not callable(mv): print( 'Input mv was not a function. Both mv and rmv shoud be functions, or both empty.' ) raise Exception(RuntimeError) elif not callable(rmv): print( 'Input rmv was not a function. Both mv and rmv shoud be functions, or both empty.' ) raise Exception(RuntimeError) else: # One of both inputs are needed for ConvolutionMatrix creation print( 'A was not an ndarray, and both multiplication functions A(x) and At(x) were not provided.' ) raise Exception(RuntimeError) self.m = self.shape[0] self.n = self.shape[1] self.matrix = LinearOperator(self.shape, matvec=mv, rmatvec=rmv) self.check_adjoint() def validate_input(self, b0, opts): assert (np.abs(b0) == b0).all, 'b must be real-valued and non-negative' if opts.customx0: assert np.shape(opts.customx0) == ( n, 1), 'customx0 must be a column vector of length n' def check_adjoint(self): """ Check that A and At are indeed ajoints of one another """ y = np.random.randn(self.m) Aty = self.matrix.rmatvec(y) x = np.random.randn(self.n) Ax = self.matrix.matvec(x) inner_product1 = Ax.conjugate().T @ y inner_product2 = x.conjugate().T @ Aty error = np.abs(inner_product1 - inner_product2) / np.abs(inner_product1) assert error < 1e-3, 'Invalid measurement operator: At is not the adjoint of A. Error = %.1f' % error print('Both matrices were adjoints', error) def hermitic(self): return def lsqr(self, b, tol, maxit, x0): """ Solution of the least squares problem for ConvolutionMatrix Gkp, opts.tol/100, opts.max_inner_iters, gk """ if b.shape[1] > 0: b = b.reshape(-1) if x0.shape[1] > 0: x0 = x0.reshape(-1) # x, istop, itn, r1norm = lsqr(self.matrix, b, atol=tol, btol=tol, iter_lim=maxit, x0=x0) ret = lsqr(self.matrix, b, damp=0.01, atol=tol / 100, btol=tol / 100, iter_lim=maxit, x0=x0) x = ret[0] return x def hmul(self, x): """ Hermitic mutliplication returns At*x """ return self.matrix.rmatvec(x) def __mul__(self, x): return self.matrix.matvec(x) def __matmul__(self, x): """Implementation of left ConvolutionMatrix multiplication, i.e. A@x""" return self.matrix.dot(x) def __rmatmul__(self, x): """Implementation of right ConvolutionMatrix multiplication, i.e. x@A""" return def __rmul__(self, x): if type(x) is float: lvec = np.ones(self.shape[1]) * x else: lvec = x return x * self.A # This is not optimal def calc_yeigs(self, m, b0, idx, squared=True): if squared: v = (idx * b0**2).reshape(-1) else: v = (idx * b0).reshape(-1) def ymatvec(x): return 1 / m * self.matrix.rmatvec(v * self.matrix.matvec(x)) yfun = LinearOperator((self.n, self.n), matvec=ymatvec) [eval, x0] = eigs(yfun, k=1, which='LR', tol=1E-5) return x0
def right_eigvec(self, k=1, tol=1e-10, init=None): """ Compute dominant right eigenvector of transfermatrix with tensors A. """ # Build list of correct matrices A = [self.mps1.B[i] for i in range(self.mps1.L)] B = [self.mps2.B[i] for i in range(self.mps2.L)] def __apply_right(vec): # Reshape vec vec = np.reshape(vec, (A[-1].shape[2], B[-1].shape[2])) # Contract as if transfer matrix vec = np.tensordot(vec, A[-1], axes=(0, 2)) # (rt rb)(d lt rt) -> (rb d lt) vec = np.tensordot(vec, np.conjugate(B[-1]), axes=((0, 1), (2, 0))) # (rb d lt)(d lb rb) -> (lt lb) if len(A) > 1: for s in range(len(A) - 2, -1, -1): vec = np.tensordot(vec, A[s], axes=(0, 2)) vec = np.tensordot(vec, np.conjugate(B[s]), axes=((0, 1), (2, 0))) return np.reshape(vec, A[0].shape[1] * B[0].shape[1]) E = LinearOperator( (A[0].shape[1] * B[0].shape[1], A[-1].shape[2] * B[-1].shape[2]), matvec=__apply_right, dtype=np.complex128) if init == None: newinit = np.random.rand(A[-1].shape[2], B[-1].shape[2]) + 1j * np.random.rand( A[-1].shape[2], B[-1].shape[2]) if A[-1].shape[2] == B[-1].shape[2]: newinit = 0.5 * (newinit + np.conjugate(np.transpose(newinit))) else: if init.shape[0] != A[-1].shape[2] or init.shape[1] != B[-1].shape[ 2]: newinit = np.zeros((A[-1].shape[2], B[-1].shape[2]), dtype=np.complex128) newinit[:init.shape[0], :init.shape[1]] = init else: newinit = init init = np.reshape(newinit, A[-1].shape[2] * B[-1].shape[2]) if E.shape[0] == 1 and E.shape[1] == 1: return E.matvec(np.array([1])), np.array([1]) ev, eigvec = sp.sparse.linalg.eigs(E, k=k, which='LM', v0=init, maxiter=1e4, tol=tol) return ev, np.array([ np.array(np.reshape(eigvec[:, i], (A[0].shape[1], B[0].shape[1]))) for i in range(k) ])
def left_eigvec(self, k=1, tol=1e-10, init=None): """ Compute dominant left eigenvector of transfermatrix with tensor A. """ # Build list of correct matrices A = [ np.tensordot( np.diag(self.mps1.Lambda[(i - 1) % self.mps1.L]), np.tensordot(self.mps1.B[i], np.diag(self.mps1.Lambda[i]**(-1)))) for i in range(self.mps1.L) ] B = [ np.tensordot( np.diag(self.mps2.Lambda[(i - 1) % self.mps2.L]), np.tensordot(self.mps2.B[i], np.diag(self.mps2.Lambda[i]**(-1)))) for i in range(self.mps2.L) ] def __apply_left(vec): # Reshape vec vec = np.reshape(vec, (A[0].shape[1], B[0].shape[1])) # Contract as if transfer matrix vec = np.tensordot(vec, A[0], axes=(0, 1)) # (lt lb)(d lt rt) -> (lb d rt) vec = np.tensordot(vec, np.conjugate(B[0]), axes=((0, 1), (1, 0))) #(lb d rt)(d lb rb) -> (rt rb) if len(A) > 1: for s in range(1, len(A)): vec = np.tensordot(vec, A[s], axes=(0, 1)) vec = np.tensordot(vec, np.conjugate(B[s]), axes=((0, 1), (1, 0))) return np.reshape(vec, A[-1].shape[2] * B[-1].shape[2]) E = LinearOperator( (A[-1].shape[2] * B[-1].shape[2], A[0].shape[1] * B[0].shape[1]), matvec=__apply_left, dtype=np.complex128) if init == None: init = np.random.rand(A[0].shape[1], B[0].shape[1]) + 1j * np.random.rand( A[0].shape[1], B[0].shape[1]) # Hermitian initial guess! if A[0].shape[0] == B[0].shape[1]: init = 0.5 * (init + np.conjugate(np.transpose(init))) init = np.reshape(init, A[0].shape[1] * B[0].shape[1]) if E.shape[0] == 1 and E.shape[1] == 1: return E.matvec(np.array([1])), np.array([1]) ev, eigvec = sp.sparse.linalg.eigs(E, k=k, which='LM', v0=init, maxiter=1e4, tol=tol) return ev, np.array([ np.array(np.reshape(eigvec[:, i], (A[-1].shape[2], B[-1].shape[2]))) for i in range(k) ])
def vcycle_stat(nu, nu1, nu2, m, u_guess, f, level): """ Function to run one vcycle of the multigrid method using the stationary method as a smoother. In this case we try to solve the initial system. Parameters ---------- nu : positive real number regularization parameter nu1 : integer number of pre-smoothing steps used nu2 : integer number of post-smoothing steps used m : integer size of the current matrix u_guess : ndarray initial guess of the current run f : ndarray function of the current right-hand side of the system level : int maximum number of levels the algorithm should do in the recursion Returns ------- u : ndarray solution after the last post-smoothing step res_norm : list list with the norm of the residuals """ norm = create_norm(m) #construct linear operator op = get_system(m, nu) C = LinearOperator((m**2, m**2), op) if level == 1: system = np.zeros((m**2, m**2)) for i in range(0, m**2): e = np.zeros(m**2) e[i] = 1 system[i, :] = C.matvec(e) u_sol = np.linalg.solve(system, f) ####### res_norm = norm(f - C(u_sol)) return (u_sol, res_norm) else: ### PRE-SMOOTHING u_nu1, _, _ = solver_stationary_fixedRight(u_guess, nu, f, m, maxIter=nu1) ### RECURSION # restriction matrix (full weighted restriction matrix) to get # the smaller system R = RMatrix(m) # projection matrix, to get back to the full system P = 4 * R.T res_temp = f - C(u_nu1) f_new = R.dot(res_temp) # get new matrix size m_new = int((m + 1) / 2 - 1) ## init with zero vector u_init = np.zeros(m_new**2) u_temp, _ = vcycle_stat(nu, nu1, nu2, m_new, u_init, f_new, level - 1) # project the current solution into the higher dimensional space u_new = u_nu1 + P.dot(u_temp) ### POST-SMOOTHING u_nu2, _, _ = solver_stationary_fixedRight(u_new, nu, f, m, maxIter=nu2) res_norm = norm(f - C(u_nu2)) return (u_nu2, res_norm)
pylab.colorbar() pylab.show() exit() #test Sigma->gamma if False: print "testing Sigma->gamma" def matvec(v): v1 = P_kd.matvec(v) return P_gk.matvec(v1) M = LinearOperator(P_kd.shape,matvec=matvec,dtype=complex) kappa_test = P_kd.matvec(Sigma) gamma_test = M.matvec(Sigma.vec) gamma_test = P_kd.view_as_Lens3D_vec(gamma_test) for i in range(gamma.Nz): pylab.figure( figsize=(6,8) ) pylab.subplot(211) kappa.imshow_lens_plane(i) gamma.fieldplot_lens_plane(i) pylab.title(r"$\kappa,\gamma\ \rm{true}\ (z=%.2f)$" % z_gamma[i]) pylab.subplot(212) kappa_test.imshow_lens_plane(i) gamma_test.fieldplot_lens_plane(i) pylab.title(r"$\kappa,\gamma\ \rm{test}\ (z=%.2f)$" % z_gamma[i]) pylab.show() exit()
return Av L_op = LinearOperator((N, N), matvec=LinOp_func) # creating the dense version of A as above A = np.zeros_like(A_dense) A += np.diag(np.diag(A_dense)) A += np.diag(np.diag(A_dense, k=diag_shift), k=diag_shift) A += np.diag(np.diag(A_dense, k=-diag_shift), k=-diag_shift) print('A:\n', A) # calculating the product using LinearOperator prod_linOp = L_op.matvec(np.ones(N)) # crudely calculating the product prod_crude = A * np.ones((N, 1)) # raises error if they are not equal. Else nothing is printed np.testing.assert_array_almost_equal(np.reshape(prod_linOp, (N, 1)), prod_crude) # solving the eigenvalue problem # using numpy.linalg.eig eigval_1, __ = np.linalg.eig(A) # using the scipy.sparse.linalg.eigs eigval_2, __ = scipy.sparse.linalg.eigsh(L_op, k=N - 1, which='SM') print('\n\n')
def make_kle(self, KLE_sigma, KLE_L, KLE_num_eig, nrow, ncol, nlay, Lx, Ly): print "making KLE, this will take a long time for large dimensions" print KLE_sigma, KLE_L, KLE_num_eig, nrow, ncol, nlay, Lx, Ly # initialise swig wrapped cpp class C_matrix = correlation.C_matrix(KLE_sigma, KLE_L) C_matrix.set_dims(nrow, ncol, Lx, Ly) out_vec = np.zeros((nrow*ncol), 'd') def KLE_Av(v): C_matrix.av_no_C(nrow*ncol, v, out_vec) return out_vec KLE_A = LinearOperator((nrow*ncol,nrow*ncol), matvec=KLE_Av, dtype='d') t1 = time.time() eig_vals, eig_vecs = eigsh( KLE_A, k=KLE_num_eig) t2 = time.time() # sometimes one gets -v rather than v? for i in range(KLE_num_eig): print "NORM", np.linalg.norm(eig_vecs[:,i]) assert np.allclose(KLE_A.matvec(eig_vecs[:,i]), eig_vals[i]*eig_vecs[:,i]) print "==================================" print "SVD took ", t2-t1, "seconds and " print "they seem to indeed be eigen vectors" print "==================================" # plot eigenvectors from mpl_toolkits.mplot3d import axes3d import matplotlib.pyplot as plt fig = plt.figure(figsize=plt.figaspect(0.2)) for i in range(eig_vecs.shape[1]): ax = fig.add_subplot(1, eig_vecs.shape[1], i, projection='3d') x = np.arange(0, nrow*Lx, Lx) X = np.empty((nrow, ncol)) for col in range(ncol): X[:,col] = x y = np.arange(0, ncol*Ly, Ly) Y = np.empty((nrow, ncol)) for row in range(nrow): Y[row,:] = y Z = eig_vecs[:,i].reshape((nrow, ncol)) ax.plot_wireframe(X, Y, Z) plt.show() # plot eigenvals # import matplotlib.pyplot as plt plt.plot(eig_vals, 'o-') plt.show() print "eig_vals", eig_vals np.savez(working_directory+'kle.npz', eig_vals=eig_vals, eig_vecs=eig_vecs, KLE_L=KLE_L, KLE_sigma=KLE_sigma, KLE_num_eig=KLE_num_eig, nrow=nrow, ncol=ncol, Lx=Lx, Ly=Ly) return eig_vals, eig_vecs
# how you'd use it ########################################## # mu = np.zeros(nrow*ncol) # could be the mean of any data you have # scale = 1. # # evaluate KLE with given modes # # other parameters (other than modes) are mu, scale, eig_vecs, eig_vals (or really sigma and L on which they depend) # def KLE(modes): # coefs = np.sqrt(eig_vals) * modes # elementwise # truncated_M = mu + scale * np.dot( eig_vecs, coefs) # unflattened = truncated_M.reshape(nrow,ncol) # return unflattened # modes = np.ones(num_eig) # print KLE(modes) ############################################ print "==================================" print "done in ", t1-t0, "seconds" print "==================================" for i in range(num_eig): assert np.allclose(A.matvec(eig_vecs[:,i]), eig_vals[i]*eig_vecs[:,i]) print "==================================" print "they are indeed eigenvectors" print "=================================="