def sparse_chol(M, perm=None, retF=False, retP=True, retL=True): # Quick check that M is square if M.size[0] != M.size[1]: raise Exception('M must be square.') # Set the factorisation to use LL' instead of LDL' cholmod.options['supernodal'] = 2 if not perm is None: # Make an expression for the factorisation F = cholmod.symbolic(M, p=perm) else: # Make an expression for the factorisation F = cholmod.symbolic(M) # Calculate the factorisation cholmod.numeric(M, F) # Empty factorisation object factorisation = {} if retF: # Calculate the factorisation again (buggy if returning L for # some reason) F2 = cholmod.symbolic(M, p=perm) cholmod.numeric(M, F2) # If we want to return the F object, add it to the dictionary factorisation['F'] = F2 if retP: # Set p to [0,...,n-1] P = cvxopt.matrix(range(M.size[0]), (M.size[0], 1), tc='d') # Solve and replace p with the true permutation used cholmod.solve(F, P, sys=7) # Convert p into an integer array; more useful that way P = cvxopt.matrix(np.array(P).astype(np.int64), tc='i') # If we want to return the permutation, add it to the dictionary factorisation['P'] = P if retL: # Get the sparse cholesky factor L = cholmod.getfactor(F) # If we want to return the factor, add it to the dictionary factorisation['L'] = L # Return P and L return (factorisation)
def embed_SDP(P,order="AMD",cholmod=False): if not isinstance(P,SDP): raise ValueError, "not an SDP object" if order=='AMD': from cvxopt.amd import order elif order=='METIS': from cvxopt.metis import order else: raise ValueError, "unknown ordering: %s " %(order) p = order(P.V) if cholmod: from cvxopt import cholmod V = +P.V + spmatrix([float(i+1) for i in xrange(P.n)],xrange(P.n),xrange(P.n)) F = cholmod.symbolic(V,p=p) cholmod.numeric(V,F) f = cholmod.getfactor(F) fd = [(j,i) for i,j in enumerate(f[:P.n**2:P.n+1])] fd.sort() ip = matrix([j for _,j in fd]) Ve = chompack.tril(chompack.perm(chompack.symmetrize(f),ip)) Ie = misc.sub2ind((P.n,P.n),Ve.I,Ve.J) else: #Vc,n = chompack.embed(P.V,p) symb = chompack.symbolic(P.V,p) #Ve = chompack.sparse(Vc) Ve = symb.sparsity_pattern(reordered=False) Ie = misc.sub2ind((P.n,P.n),Ve.I,Ve.J) Pe = SDP() Pe._A = +P.A; Pe._b = +P.b Pe._A[:,0] += spmatrix(0.0,Ie,[0 for i in range(len(Ie))],(Pe._A.size[0],1)) Pe._agg_sparsity() Pe._pname = P._pname + "_embed" Pe._ischordal = True; Pe._blockstruct = P._blockstruct return Pe
def eval(obj): try: s = cholmod.options['supernodal'] except KeyError: s = 2 # set to default. cholmod.options['supernodal'] = 0 A = cvxopt.base.sparse(obj) F = cholmod.symbolic(A) cholmod.numeric(A, F) Di = matrix(1.0, (4, 1)) cholmod.solve(F, Di, sys=6) # Reset state. cholmod.options['supernodal'] = s return -sum(cvxopt.base.log(Di))
def eval(obj): try: s = cholmod.options['supernodal'] except KeyError: s = 2 # set to default. cholmod.options['supernodal'] = 0 A = cvxopt.base.sparse(obj) F = cholmod.symbolic(A) cholmod.numeric(A, F) Di = matrix(1.0, (4,1)) cholmod.solve(F, Di, sys=6) # Reset state. cholmod.options['supernodal'] = s return -sum(cvxopt.base.log(Di))
def sp_det_sym(M): # Change to the LL' decomposition prevopts = cholmod.options['supernodal'] cholmod.options['supernodal'] = 2 # Obtain decomposition of M F = cholmod.symbolic(M) cholmod.numeric(M, F) # Restore previous options cholmod.options['supernodal'] = prevopts # As PMP'=LL' and det(P)=1 for all permutations # it follows det(M)=det(L)^2=product(diag(L))^2 return (np.exp(2 * sum(cvxopt.log(cholmod.diag(F)))))
def covsel(Y): """ Returns the solution of minimize -log det K + Tr(KY) subject to K_{ij}=0, (i,j) not in indices listed in I,J. Y is a symmetric sparse matrix with nonzero diagonal elements. I = Y.I, J = Y.J. """ I, J = Y.I, Y.J n, m = Y.size[0], len(I) N = I + J*n # non-zero positions for one-argument indexing D = [k for k in range(m) if I[k]==J[k]] # position of diagonal elements # starting point: symmetric identity with nonzero pattern I,J K = spmatrix(0.0, I, J) K[::n+1] = 1.0 # Kn is used in the line search Kn = spmatrix(0.0, I, J) # symbolic factorization of K F = cholmod.symbolic(K) # Kinv will be the inverse of K Kinv = matrix(0.0, (n,n)) for iters in range(100): # numeric factorization of K cholmod.numeric(K, F) d = cholmod.diag(F) # compute Kinv by solving K*X = I Kinv[:] = 0.0 Kinv[::n+1] = 1.0 cholmod.solve(F, Kinv) # solve Newton system grad = 2*(Y.V - Kinv[N]) hess = 2*(mul(Kinv[I,J],Kinv[J,I]) + mul(Kinv[I,I],Kinv[J,J])) v = -grad lapack.posv(hess,v) # stopping criterion sqntdecr = -blas.dot(grad,v) print("Newton decrement squared:%- 7.5e" %sqntdecr) if (sqntdecr < 1e-12): print("number of iterations: ", iters+1) break # line search dx = +v dx[D] *= 2 # scale the diagonal elems f = -2.0 * sum(log(d)) # f = -log det K s = 1 for lsiter in range(50): Kn.V = K.V + s*dx try: cholmod.numeric(Kn, F) except ArithmeticError: s *= 0.5 else: d = cholmod.diag(F) fn = -2.0 * sum(log(d)) + 2*s*blas.dot(v,Y.V) if (fn < f - 0.01*s*sqntdecr): break s *= 0.5 K.V = Kn.V return K
# Read in Y Y = pandas.read_csv('Y.csv', header=None).values # Read in ffx variance sigma2 = pandas.read_csv('true_ffxvar.csv', header=None).values ZtZ = cvxopt.spmatrix.trans(Z2) * Z2 # Use minimum degree ordering P = amd.order(ZtZ) # Set the factorisation to use LL' instead of LDL' cholmod.options['supernodal'] = 2 # Make an expression for the factorisation F = cholmod.symbolic(ZtZ, p=P) # Calculate the factorisation cholmod.numeric(ZtZ, F) # Get the sparse cholesky factorisation L = cholmod.getfactor(F) LLt = L * cvxopt.spmatrix.trans(L) # Create initial lambda # Work out how many lambda components needed f1_n_lamcomps = np.int64(f1_nv * (f1_nv + 1) / 2) f2_n_lamcomps = np.int64(f2_nv * (f2_nv + 1) / 2) # Work out triangular indices for lambda (blocks starting from (0,0)) r_inds_f1_block, c_inds_f1_block = np.tril_indices(f1_nv)
def solve(A, b, C, L, dims, proxqp=None, sigma=1.0, rho=1.0, **kwargs): """ Solves the SDP min. < c, x > s.t. A(x) = b x >= 0 and its dual max. -< b, y > s.t. s >= 0. c + A'(y) = s Input arguments. A is an N x M sparse matrix where N = sum_i ns[i]**2 and M = sum_j ms[j] and ns and ms are the SDP variable sizes and constraint block lengths respectively. The expression A(x) = b can be written as A.T*xtilde = b, where xtilde is a stacked vector of vectorized versions of xi. b is a stacked vector containing constraint vectors of size m_i x 1. C is a stacked vector containing vectorized 'd' matrices c_k of size n_k**2 x 1, representing symmetric matrices. L is an N X P sparse matrix, where L.T*X = 0 represents the consistency constraints. If an index k appears in different cliques i,j, and in converted form are indexed by it, jt, then L[it,l] = 1, L[jt,l] = -1 for some l. dims is a dictionary containing conic dimensions. dims['l'] contains number of linear variables under nonnegativity constrant dims['q'] contains a list of quadratic cone orders (not implemented!) dims['s'] contains a list of semidefinite cone matrix orders proxqp is either a function pointer to a prox implementation, or, if the problem has block-diagonal correlative sparsity, a pointer to the prox implementation of a single clique. The choices are: proxqp_general : solves prox for general sparsity pattern proxqp_clique : solves prox for a single dense clique with only semidefinite variables. proxqp_clique_SNL : solves prox for sensor network localization problem sigma is a nonnegative constant (step size) rho is a nonnegative constaint between 0 and 2 (overrelaxation parameter) In addition, the following paramters are optional: maxiter : maximum number of iterations (default 100) reltol : relative tolerance (default 0.01). If rp < reltol and rd < reltol and iteration < maxiter, solver breaks and returns current value. adaptive : boolean toggle on whether adaptive step size should be used. (default False) mu, tau, tauscale : parameters for adaptive step size (see paper) multiprocess : number of parallel processes (default 1). if multiprocess = 1, no parallelization is used. blockdiagonal : boolean toggle on whether problem has block diagonal correlative sparsity. Note that even if the problem does have block-diagonal correlative sparsity, if this parameter is set to False, then general mode is used. (default False) verbose : toggle printout (default True) log_cputime : toggle whether cputime should be logged. The output is returned in a dictionary with the following files: x : primal variable in stacked form (X = [x0, ..., x_{N-1}]) where xk is the vectorized form of the nk x nk submatrix variable. y, z : iterates in Spingarn's method cputime, walltime : total cputime and walltime, respectively, spent in main loop. If log_cputime is False, then cputime is returned as 0. primal, rprimal, rdual : evolution of primal optimal value, primal residual, and dual residual (resp.) sigma : evolution of step size sigma (changes if adaptive step size is used.) """ solvers.options['show_progress'] = False maxiter = kwargs.get('maxiter', 100) reltol = kwargs.get('reltol', 0.01) adaptive = kwargs.get('adaptive', False) mu = kwargs.get('mu', 2.0) tau = kwargs.get('tau', 1.5) multiprocess = kwargs.get('multiprocess', 1) tauscale = kwargs.get('tauscale', 0.9) blockdiagonal = kwargs.get('blockdiagonal', False) verbose = kwargs.get('verbose', True) log_cputime = kwargs.get('log_cputime', True) if log_cputime: try: import psutil except (ImportError): assert False, "Python package psutil required to log cputime. Package can be downloaded at http://code.google.com/p/psutil/" #format variables nl, ns = dims['l'], dims['s'] C = C[nl:] L = L[nl:, :] As, bs = [], [] cons = [] offset = 0 for k in xrange(len(ns)): Atmp = sparse(A[nl + offset:nl + offset + ns[k]**2, :]) J = list(set(list(Atmp.J))) Atmp = Atmp[:, J] if len(sparse(Atmp).V) == Atmp[:].size[0]: Atmp = matrix(Atmp) else: Atmp = sparse(Atmp) As.append(Atmp) bs.append(b[J]) cons.append(J) offset += ns[k]**2 if blockdiagonal: if sum([len(c) for c in cons]) > len(b): print "Problem does not have block-diagonal correlative sparsity. Switching to general mode." blockdiagonal = False #If not block-diagonal correlative sprasity, represent A as a list of lists: # A[i][j] is a matrix (or spmatrix) if ith clique involves jth constraint block #Otherwise, A is a list of matrices, where A[i] involves the ith clique and #ith constraint block only. if not blockdiagonal: while sum([len(c) for c in cons]) > len(b): tobreak = False for i in xrange(len(cons)): for j in xrange(i): ci, cj = set(cons[i]), set(cons[j]) s1 = ci.intersection(cj) if len(s1) > 0: s2 = ci.difference(cj) s3 = cj.difference(ci) cons.append(list(s1)) if len(s2) > 0: s2 = list(s2) if not (s2 in cons): cons.append(s2) if len(s3) > 0: s3 = list(s3) if not (s3 in cons): cons.append(s3) cons.pop(i) cons.pop(j) tobreak = True break if tobreak: break As, bs = [], [] for i in xrange(len(cons)): J = cons[i] bs.append(b[J]) Acol = [] offset = 0 for k in xrange(len(ns)): Atmp = sparse(A[nl + offset:nl + offset + ns[k]**2, J]) if len(Atmp.V) == 0: Acol.append(0) elif len(Atmp.V) == Atmp[:].size[0]: Acol.append(matrix(Atmp)) else: Acol.append(Atmp) offset += ns[k]**2 As.append(Acol) ms = [len(i) for i in bs] bs = matrix(bs) meq = L.size[1] if (not blockdiagonal) and multiprocess > 1: print "Multiprocessing mode can only be used if correlative sparsity is block diagonal. Switching to sequential mode." multiprocess = 1 assert rho > 0 and rho < 2, 'Overrelaxaton parameter (rho) must be (strictly) between 0 and 2' # create routine for projecting on { x | L*x = 0 } #{ x | L*x = 0 } -> P = I - L*(L.T*L)i *L.T LTL = spmatrix([], [], [], (meq, meq)) offset = 0 for k in ns: Lk = L[offset:offset + k**2, :] base.syrk(Lk, LTL, trans='T', beta=1.0) offset += k**2 LTLi = cholmod.symbolic(LTL, amd.order(LTL)) cholmod.numeric(LTL, LTLi) #y = y - L*LTLi*L.T*y nssq = sum(matrix([nsk**2 for nsk in ns])) def proj(y, ip=True): if not ip: y = +y tmp = matrix(0.0, size=(meq, 1)) ypre = +y base.gemv(L,y,tmp,trans='T',\ m = nssq, n = meq, beta = 1) cholmod.solve(LTLi, tmp) base.gemv(L,tmp,y,beta=1.0,alpha=-1.0,trans='N',\ m = nssq, n = meq) if not ip: return y time_to_solve = 0 #initialize variables X = C * 0.0 Y = +X Z = +X dualS = +X dualy = +b PXZ = +X proxargs = { 'C': C, 'A': As, 'b': bs, 'Z': Z, 'X': X, 'sigma': sigma, 'dualS': dualS, 'dualy': dualy, 'ns': ns, 'ms': ms, 'multiprocess': multiprocess } if blockdiagonal: proxqp = proxqp_blockdiagonal(proxargs, proxqp) else: proxqp = proxqp_general if log_cputime: utime = psutil.cpu_times()[0] wtime = time.time() primal = [] rpvec, rdvec = [], [] sigmavec = [] for it in xrange(maxiter): pv, gap = proxqp(proxargs) blas.copy(Z, Y) blas.axpy(X, Y, alpha=-2.0) proj(Y, ip=True) #PXZ = sigma*(X-Z) blas.copy(X, PXZ) blas.scal(sigma, PXZ) blas.axpy(Z, PXZ, alpha=-sigma) #z = z + rho*(y-x) blas.axpy(X, Y, alpha=1.0) blas.axpy(Y, Z, alpha=-rho) xzn = blas.nrm2(PXZ) xn = blas.nrm2(X) xyn = blas.nrm2(Y) proj(PXZ, ip=True) rdual = blas.nrm2(PXZ) rpri = sqrt(abs(xyn**2 - rdual**2)) / sigma if log_cputime: cputime = psutil.cpu_times()[0] - utime else: cputime = 0 walltime = time.time() - wtime if rpri / max(xn, 1.0) < reltol and rdual / max(1.0, xzn) < reltol: break rpvec.append(rpri / max(xn, 1.0)) rdvec.append(rdual / max(1.0, xzn)) primal.append(pv) if adaptive: if (rdual / xzn * mu < rpri / xn): sigmanew = sigma * tau elif (rpri / xn * mu < rdual / xzn): sigmanew = sigma / tau else: sigmanew = sigma if it % 10 == 0 and it > 0 and tau > 1.0: tauscale *= 0.9 tau = 1 + (tau - 1) * tauscale sigma = max(min(sigmanew, 10.0), 0.1) sigmavec.append(sigma) if verbose: if log_cputime: print "%d: primal = %e, gap = %e, (rp,rd) = (%e,%e), sigma = %f, (cputime,walltime) = (%f, %f)" % ( it, pv, gap, rpri / max(xn, 1.0), rdual / max(1.0, xzn), sigma, cputime, walltime) else: print "%d: primal = %e, gap = %e, (rp,rd) = (%e,%e), sigma = %f, walltime = %f" % ( it, pv, gap, rpri / max(xn, 1.0), rdual / max(1.0, xzn), sigma, walltime) sol = {} sol['x'] = X sol['y'] = Y sol['z'] = Z sol['cputime'] = cputime sol['walltime'] = walltime sol['primal'] = primal sol['rprimal'] = rpvec sol['rdual'] = rdvec sol['sigma'] = sigmavec return sol
def factor(W, H=None, Df=None): if F['firstcall']: if type(G) is matrix: F['Gs'] = matrix(0.0, G.size) else: F['Gs'] = spmatrix(0.0, G.I, G.J, G.size) if mnl: if type(Df) is matrix: F['Dfs'] = matrix(0.0, Df.size) else: F['Dfs'] = spmatrix(0.0, Df.I, Df.J, Df.size) if (mnl and type(Df) is matrix) or type(G) is matrix or \ type(H) is matrix: F['S'] = matrix(0.0, (n, n)) F['K'] = matrix(0.0, (p, p)) else: F['S'] = spmatrix([], [], [], (n, n), 'd') F['Sf'] = None if type(A) is matrix: F['K'] = matrix(0.0, (p, p)) else: F['K'] = spmatrix([], [], [], (p, p), 'd') # Dfs = Wnl^{-1} * Df if mnl: base.gemm(spmatrix(W['dnli'], list(range(mnl)), list(range(mnl))), Df, F['Dfs'], partial=True) # Gs = Wl^{-1} * G. base.gemm(spmatrix(W['di'], list(range(ml)), list(range(ml))), G, F['Gs'], partial=True) if F['firstcall']: base.syrk(F['Gs'], F['S'], trans='T') if mnl: base.syrk(F['Dfs'], F['S'], trans='T', beta=1.0) if H is not None: F['S'] += H try: if type(F['S']) is matrix: lapack.potrf(F['S']) else: F['Sf'] = cholmod.symbolic(F['S']) cholmod.numeric(F['S'], F['Sf']) except ArithmeticError: F['singular'] = True if type(A) is matrix and type(F['S']) is spmatrix: F['S'] = matrix(0.0, (n, n)) base.syrk(F['Gs'], F['S'], trans='T') if mnl: base.syrk(F['Dfs'], F['S'], trans='T', beta=1.0) base.syrk(A, F['S'], trans='T', beta=1.0) if H is not None: F['S'] += H if type(F['S']) is matrix: lapack.potrf(F['S']) else: F['Sf'] = cholmod.symbolic(F['S']) cholmod.numeric(F['S'], F['Sf']) F['firstcall'] = False else: base.syrk(F['Gs'], F['S'], trans='T', partial=True) if mnl: base.syrk(F['Dfs'], F['S'], trans='T', beta=1.0, partial=True) if H is not None: F['S'] += H if F['singular']: base.syrk(A, F['S'], trans='T', beta=1.0, partial=True) if type(F['S']) is matrix: lapack.potrf(F['S']) else: cholmod.numeric(F['S'], F['Sf']) if type(F['S']) is matrix: # Asct := L^{-1}*A'. Factor K = Asct'*Asct. if type(A) is matrix: Asct = A.T else: Asct = matrix(A.T) blas.trsm(F['S'], Asct) blas.syrk(Asct, F['K'], trans='T') lapack.potrf(F['K']) else: # Asct := L^{-1}*P*A'. Factor K = Asct'*Asct. if type(A) is matrix: Asct = A.T cholmod.solve(F['Sf'], Asct, sys=7) cholmod.solve(F['Sf'], Asct, sys=4) blas.syrk(Asct, F['K'], trans='T') lapack.potrf(F['K']) else: Asct = cholmod.spsolve(F['Sf'], A.T, sys=7) Asct = cholmod.spsolve(F['Sf'], Asct, sys=4) base.syrk(Asct, F['K'], trans='T') Kf = cholmod.symbolic(F['K']) cholmod.numeric(F['K'], Kf) def solve(x, y, z): # Solve # # [ H A' GG'*W^{-1} ] [ ux ] [ bx ] # [ A 0 0 ] * [ uy ] = [ by ] # [ W^{-T}*GG 0 -I ] [ W*uz ] [ W^{-T}*bz ] # # and return ux, uy, W*uz. # # If not F['singular']: # # K*uy = A * S^{-1} * ( bx + GG'*W^{-1}*W^{-T}*bz ) - by # S*ux = bx + GG'*W^{-1}*W^{-T}*bz - A'*uy # W*uz = W^{-T} * ( GG*ux - bz ). # # If F['singular']: # # K*uy = A * S^{-1} * ( bx + GG'*W^{-1}*W^{-T}*bz + A'*by ) # - by # S*ux = bx + GG'*W^{-1}*W^{-T}*bz + A'*by - A'*y. # W*uz = W^{-T} * ( GG*ux - bz ). # z := W^{-1} * z = W^{-1} * bz scale(z, W, trans='T', inverse='I') # If not F['singular']: # x := L^{-1} * P * (x + GGs'*z) # = L^{-1} * P * (x + GG'*W^{-1}*W^{-T}*bz) # # If F['singular']: # x := L^{-1} * P * (x + GGs'*z + A'*y)) # = L^{-1} * P * (x + GG'*W^{-1}*W^{-T}*bz + A'*y) if mnl: base.gemv(F['Dfs'], z, x, trans='T', beta=1.0) base.gemv(F['Gs'], z, x, offsetx=mnl, trans='T', beta=1.0) if F['singular']: base.gemv(A, y, x, trans='T', beta=1.0) if type(F['S']) is matrix: blas.trsv(F['S'], x) else: cholmod.solve(F['Sf'], x, sys=7) cholmod.solve(F['Sf'], x, sys=4) # y := K^{-1} * (Asc*x - y) # = K^{-1} * (A * S^{-1} * (bx + GG'*W^{-1}*W^{-T}*bz) - by) # (if not F['singular']) # = K^{-1} * (A * S^{-1} * (bx + GG'*W^{-1}*W^{-T}*bz + # A'*by) - by) # (if F['singular']). base.gemv(Asct, x, y, trans='T', beta=-1.0) if type(F['K']) is matrix: lapack.potrs(F['K'], y) else: cholmod.solve(Kf, y) # x := P' * L^{-T} * (x - Asc'*y) # = S^{-1} * (bx + GG'*W^{-1}*W^{-T}*bz - A'*y) # (if not F['singular']) # = S^{-1} * (bx + GG'*W^{-1}*W^{-T}*bz + A'*by - A'*y) # (if F['singular']) base.gemv(Asct, y, x, alpha=-1.0, beta=1.0) if type(F['S']) is matrix: blas.trsv(F['S'], x, trans='T') else: cholmod.solve(F['Sf'], x, sys=5) cholmod.solve(F['Sf'], x, sys=8) # W*z := GGs*x - z = W^{-T} * (GG*x - bz) if mnl: base.gemv(F['Dfs'], x, z, beta=-1.0) base.gemv(F['Gs'], x, z, beta=-1.0, offsety=mnl) return solve
def F(W): for j in xrange(N): # SVD R[j] = U[j] * diag(sig[j]) * Vt[j] lapack.gesvd(+W['r'][j], sv[j], jobu='A', jobvt='A', U=U[j], Vt=Vt[j]) # Vt[j] := diag(sig[j])^-1 * Vt[j] for k in xrange(ns[j]): blas.tbsv(sv[j], Vt[j], n=ns[j], k=0, ldA=1, offsetx=k * ns[j]) # Gamma[j] is an ns[j] x ns[j] symmetric matrix # # (sig[j] * sig[j]') ./ sqrt(1 + rho * (sig[j] * sig[j]').^2) # S = sig[j] * sig[j]' S = matrix(0.0, (ns[j], ns[j])) blas.syrk(sv[j], S) Gamma[j][:] = div(S, sqrt(1.0 + rho * S**2))[:] symmetrize(Gamma[j], ns[j]) # As represents the scaled mapping # # As(x) = A(u * (Gamma .* x) * u') # As'(y) = Gamma .* (u' * A'(y) * u) # # stored in a similar format as A, except that we use packed # storage for the columns of As[i][j]. for i in xrange(M): if (type(A[i][j]) is matrix) or (type(A[i][j]) is spmatrix): # As[i][j][:,k] = vec( # (U[j]' * mat( A[i][j][:,k] ) * U[j]) .* Gamma[j]) copy(A[i][j], As[i][j]) As[i][j] = matrix(As[i][j]) for k in xrange(ms[i]): cngrnc(U[j], As[i][j], trans='T', offsetx=k * (ns[j]**2), n=ns[j]) blas.tbmv(Gamma[j], As[i][j], n=ns[j]**2, k=0, ldA=1, offsetx=k * (ns[j]**2)) # pack As[i][j] in place #pack_ip(As[i][j], ns[j]) for k in xrange(As[i][j].size[1]): tmp = +As[i][j][:, k] misc.pack2(tmp, {'l': 0, 'q': [], 's': [ns[j]]}) As[i][j][:, k] = tmp else: As[i][j] = 0.0 # H is an m times m block matrix with i, k block # # Hik = sum_j As[i,j]' * As[k,j] # # of size ms[i] x ms[k]. Hik = 0 if As[i,j] or As[k,j] # are zero for all j H = spmatrix([], [], [], (sum(ms), sum(ms))) rowid = 0 for i in xrange(M): colid = 0 for k in xrange(i + 1): sparse_block = True Hik = matrix(0.0, (ms[i], ms[k])) for j in xrange(N): if (type(As[i][j]) is matrix) and \ (type(As[k][j]) is matrix): sparse_block = False # Hik += As[i,j]' * As[k,j] if i == k: blas.syrk(As[i][j], Hik, trans='T', beta=1.0, k=ns[j] * (ns[j] + 1) / 2, ldA=ns[j]**2) else: blas.gemm(As[i][j], As[k][j], Hik, transA='T', beta=1.0, k=ns[j] * (ns[j] + 1) / 2, ldA=ns[j]**2, ldB=ns[j]**2) if not (sparse_block): H[rowid:rowid+ms[i], colid:colid+ms[k]] \ = sparse(Hik) colid += ms[k] rowid += ms[i] HF = cholmod.symbolic(H) cholmod.numeric(H, HF) def solve(x, y, z): """ Returns solution of rho * ux + A'(uy) - r^-T * uz * r^-1 = bx A(ux) = by -ux - r * uz * r' = bz. On entry, x = bx, y = by, z = bz. On exit, x = ux, y = uy, z = uz. """ # bz is a copy of z in the format of x blas.copy(z, bz) # x := x + rho * bz # = bx + rho * bz blas.axpy(bz, x, alpha=rho) # x := Gamma .* (u' * x * u) # = Gamma .* (u' * (bx + rho * bz) * u) offsetj = 0 for j in xrange(N): cngrnc(U[j], x, trans='T', offsetx=offsetj, n=ns[j]) blas.tbmv(Gamma[j], x, n=ns[j]**2, k=0, ldA=1, offsetx=offsetj) offsetj += ns[j]**2 # y := y - As(x) # := by - As( Gamma .* u' * (bx + rho * bz) * u) blas.copy(x, xp) offsetj = 0 for j in xrange(N): misc.pack2(xp, {'l': offsetj, 'q': [], 's': [ns[j]]}) offsetj += ns[j]**2 offseti = 0 for i in xrange(M): offsetj = 0 for j in xrange(N): if type(As[i][j]) is matrix: blas.gemv(As[i][j], xp, y, trans='T', alpha=-1.0, beta=1.0, m=ns[j] * (ns[j] + 1) / 2, n=ms[i], ldA=ns[j]**2, offsetx=offsetj, offsety=offseti) offsetj += ns[j]**2 offseti += ms[i] # y := -y - A(bz) # = -by - A(bz) + As(Gamma .* (u' * (bx + rho * bz) * u) Af(bz, y, alpha=-1.0, beta=-1.0) # y := H^-1 * y # = H^-1 ( -by - A(bz) + As(Gamma.* u'*(bx + rho*bz)*u) ) # = uy cholmod.solve(HF, y) # bz = Vt' * vz * Vt # = uz where # vz := Gamma .* ( As'(uy) - x ) # = Gamma .* ( As'(uy) - Gamma .* (u'*(bx + rho *bz)*u) ) # = Gamma.^2 .* ( u' * (A'(uy) - bx - rho * bz) * u ). blas.copy(x, xp) offsetj = 0 for j in xrange(N): # xp is -x[j] = -Gamma .* (u' * (bx + rho*bz) * u) # in packed storage misc.pack2(xp, {'l': offsetj, 'q': [], 's': [ns[j]]}) offsetj += ns[j]**2 blas.scal(-1.0, xp) offsetj = 0 for j in xrange(N): # xp += As'(uy) offseti = 0 for i in xrange(M): if type(As[i][j]) is matrix: blas.gemv(As[i][j], y, xp, alpha = 1.0, beta = 1.0, m = ns[j]*(ns[j]+1)/2, \ n = ms[i],ldA = ns[j]**2, \ offsetx = offseti, offsety = offsetj) offseti += ms[i] # bz[j] is xp unpacked and multiplied with Gamma #unpack(xp, bz[j], ns[j]) misc.unpack(xp, bz, { 'l': 0, 'q': [], 's': [ns[j]] }, offsetx=offsetj, offsety=offsetj) blas.tbmv(Gamma[j], bz, n=ns[j]**2, k=0, ldA=1, offsetx=offsetj) # bz = Vt' * bz * Vt # = uz cngrnc(Vt[j], bz, trans='T', offsetx=offsetj, n=ns[j]) symmetrize(bz, ns[j], offset=offsetj) offsetj += ns[j]**2 # x = -bz - r * uz * r' blas.copy(z, x) blas.copy(bz, z) offsetj = 0 for j in xrange(N): cngrnc(+W['r'][j], bz, offsetx=offsetj, n=ns[j]) offsetj += ns[j]**2 blas.axpy(bz, x) blas.scal(-1.0, x) return solve
from cvxopt import matrix, spmatrix, cholmod A = spmatrix([10, 3, 5, -2, 5, 2], [0, 2, 1, 3, 2, 3], [0, 0, 1, 1, 2, 3]) X = matrix(range(8), (4,2), 'd') F = cholmod.symbolic(A) cholmod.numeric(A, F) cholmod.solve(F, X) print(X) P = cvxopt.matrix(P) print(cvxopt.matrix(list(map(lambda x: x != 1, abs(P))), P.size))
def covsel(Y): """ Returns the solution of minimize -log det K + tr(KY) subject to K_ij = 0 if (i,j) not in zip(I, J). Y is a symmetric sparse matrix with nonzero diagonal elements. I = Y.I, J = Y.J. """ cholmod.options['supernodal'] = 2 I, J = Y.I, Y.J n, m = Y.size[0], len(I) # non-zero positions for one-argument indexing N = I + J*n # position of diagonal elements D = [ k for k in range(m) if I[k]==J[k] ] # starting point: symmetric identity with nonzero pattern I,J K = spmatrix(0.0, I, J) K[::n+1] = 1.0 # Kn is used in the line search Kn = spmatrix(0.0, I, J) # symbolic factorization of K F = cholmod.symbolic(K) # Kinv will be the inverse of K Kinv = matrix(0.0, (n,n)) for iters in range(100): # numeric factorization of K cholmod.numeric(K, F) d = cholmod.diag(F) # compute Kinv by solving K*X = I Kinv[:] = 0.0 Kinv[::n+1] = 1.0 cholmod.solve(F, Kinv) # solve Newton system grad = 2 * (Y.V - Kinv[N]) hess = 2 * ( mul(Kinv[I,J], Kinv[J,I]) + mul(Kinv[I,I], Kinv[J,J]) ) v = -grad lapack.posv(hess,v) # stopping criterion sqntdecr = -blas.dot(grad,v) print("Newton decrement squared:%- 7.5e" %sqntdecr) if (sqntdecr < 1e-12): print("number of iterations: %d" %(iters+1)) break # line search dx = +v dx[D] *= 2 f = -2.0*sum(log(d)) # f = -log det K s = 1 for lsiter in range(50): Kn.V = K.V + s*dx try: cholmod.numeric(Kn, F) except ArithmeticError: s *= 0.5 else: d = cholmod.diag(F) fn = -2.0 * sum(log(d)) + 2*s*blas.dot(v,Y.V) if (fn < f - 0.01*s*sqntdecr): break else: s *= 0.5 K.V = Kn.V return K
def solve_phase1(self,kktsolver='chol',MM = 1e5): """ Solves primal Phase I problem using the feasible start solver. Returns primal feasible X. """ from cvxopt import cholmod, amd k = 1e-3 # compute Schur complement matrix Id = [i*(self.n+1) for i in range(self.n)] As = self._A[:,1:] As[Id,:] /= sqrt(2.0) M = spmatrix([],[],[],(self.m,self.m)) base.syrk(As,M,trans='T') u = +self.b # compute least-norm solution F = cholmod.symbolic(M) cholmod.numeric(M,F) cholmod.solve(F,u) x = 0.5*self._A[:,1:]*u X0 = spmatrix(x[self.V[:].I],self.V.I,self.V.J,(self.n,self.n)) # test feasibility p = amd.order(self.V) #Xc,Nf = chompack.embed(X0,p) #E = chompack.project(Xc,spmatrix(1.0,range(self.n),range(self.n))) symb = chompack.symbolic(self.V,p) Xc = chompack.cspmatrix(symb) + X0 try: # L = chompack.completion(Xc) L = Xc.copy() chompack.completion(L) # least-norm solution is feasible return X0,None except: pass # create Phase I SDP object trA = matrix(0.0,(self.m+1,1)) e = matrix(1.0,(self.n,1)) Aa = self._A[Id,1:] base.gemv(Aa,e,trA,trans='T') trA[-1] = MM P1 = SDP() P1._A = misc.phase1_sdp(self._A,trA) P1._b = matrix([self.b-k*trA[:self.m],MM]) P1._agg_sparsity() # find feasible starting point for Phase I problem tMIN = 0.0 tMAX = 1.0 while True: t = (tMIN+tMAX)/2.0 #Xt = chompack.copy(Xc) #chompack.axpy(E,Xt,t) Xt = Xc.copy() + spmatrix(t,list(range(self.n)),list(range(self.n))) try: # L = chompack.completion(Xt) L = Xt.copy() chompack.completion(L) tMAX = t if tMAX - tMIN < 1e-1: break except: tMAX *= 2.0 tMIN = t tt = t + 1.0 U = X0 + spmatrix(tt,list(range(self.n,)),list(range(self.n))) trU = sum(U[:][Id]) Z0 = spdiag([U,spmatrix([tt+k,MM-trU],[0,1],[0,1],(2,2))]) sol = P1.solve_feas(primalstart = {'x':Z0}, kktsolver = kktsolver) s = sol['x'][-2,-2] - k if s > 0: return None,P1 else: sol.pop('y') sol.pop('s') X0 = sol.pop('x')[:self.n,:self.n]\ - spmatrix(s,list(range(self.n)),list(range(self.n))) return X0,sol
def solve_phase1(self, kktsolver='chol', MM=1e5): """ Solves primal Phase I problem using the feasible start solver. Returns primal feasible X. """ from cvxopt import cholmod, amd k = 1e-3 # compute Schur complement matrix Id = [i * (self.n + 1) for i in range(self.n)] As = self._A[:, 1:] As[Id, :] /= sqrt(2.0) M = spmatrix([], [], [], (self.m, self.m)) base.syrk(As, M, trans='T') u = +self.b # compute least-norm solution F = cholmod.symbolic(M) cholmod.numeric(M, F) cholmod.solve(F, u) x = 0.5 * self._A[:, 1:] * u X0 = spmatrix(x[self.V[:].I], self.V.I, self.V.J, (self.n, self.n)) # test feasibility p = amd.order(self.V) #Xc,Nf = chompack.embed(X0,p) #E = chompack.project(Xc,spmatrix(1.0,range(self.n),range(self.n))) symb = chompack.symbolic(self.V, p) Xc = chompack.cspmatrix(symb) + X0 try: # L = chompack.completion(Xc) L = Xc.copy() chompack.completion(L) # least-norm solution is feasible return X0 except: pass # create Phase I SDP object trA = matrix(0.0, (self.m + 1, 1)) e = matrix(1.0, (self.n, 1)) Aa = self._A[Id, 1:] base.gemv(Aa, e, trA, trans='T') trA[-1] = MM P1 = SDP() P1._A = misc.phase1_sdp(self._A, trA) P1._b = matrix([self.b - k * trA[:self.m], MM]) P1._agg_sparsity() # find feasible starting point for Phase I problem tMIN = 0.0 tMAX = 1.0 while True: t = (tMIN + tMAX) / 2.0 #Xt = chompack.copy(Xc) #chompack.axpy(E,Xt,t) Xt = Xc.copy() + spmatrix(t, range(self.n), range(self.n)) try: # L = chompack.completion(Xt) L = Xt.copy() chompack.completion(L) tMAX = t if tMAX - tMIN < 1e-1: break except: tMAX *= 2.0 tMIN = t tt = t + 1.0 U = X0 + spmatrix(tt, range(self.n, ), range(self.n)) trU = sum(U[:][Id]) Z0 = spdiag([U, spmatrix([tt + k, MM - trU], [0, 1], [0, 1], (2, 2))]) sol = P1.solve_feas(primalstart={'x': Z0}, kktsolver=kktsolver) s = sol['x'][-2, -2] - k if s > 0: return None, P1 else: sol.pop('y') sol.pop('s') X0 = sol.pop('x')[:self.n,:self.n]\ - spmatrix(s,range(self.n),range(self.n)) return X0, sol