Source code for picos.modeling.file_out

# ------------------------------------------------------------------------------
# Copyright (C) 2012-2017 Guillaume Sagnol
# Copyright (C)      2019 Maximilian Stahlberg
#
# This file is part of PICOS.
#
# PICOS is free software: you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
# Foundation, either version 3 of the License, or (at your option) any later
# version.
#
# PICOS is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with
# this program.  If not, see <http://www.gnu.org/licenses/>.
# ------------------------------------------------------------------------------

"""Functions for writing optimization problems to a file."""

# ------------------------------------------------------------------------------
# TODO: This is a temporary solution using outdated code. Ideally, writing to
#       a file should be integrated with the reformulation pipeline e.g. by
#       distinguishing a SolutionStrategy and an ExportStrategy.
# ------------------------------------------------------------------------------

from itertools import chain

import cvxopt
import numpy

from ..apidoc import api_end, api_start
from ..constraints import (
    AffineConstraint,
    LMIConstraint,
    RSOCConstraint,
    SOCConstraint,
)

from ..solvers import CVXOPTSolver, CPLEXSolver, MOSEKSolver, GurobiSolver
from ..expressions import IntegerVariable, BinaryVariable, CONTINUOUS_VARTYPES
from ..expressions.vectorizations import FullVectorization

_API_START = api_start(globals())
# -------------------------------


INFINITY = 1e16  #: A number deemed too large to appear in practice.


[docs]def write(picos_problem, filename, writer="picos"): r"""Write an optimization problem to a file. :param ~picos.Problem P: The problem to write. :param str filename: Path and name of the output file. The export format is inferred from the file extension. Supported extensions and their associated format are: * ``'.cbf'`` -- Conic Benchmark Format. This format is suitable for optimization problems involving second order and/or semidefinite cone constraints. This is a standard choice for conic optimization problems. Visit the website of `The Conic Benchmark Library <http://cblib.zib.de/>`_ or read `A benchmark library for conic mixed-integer and continuous optimization <http://www.optimization-online.org/DB_HTML/2014/03/4301.html>`_ by Henrik A. Friberg for more information. * ``'.lp'`` -- `LP format <http://docs.mosek.com/6.0/pyapi/node022.html>`_. This format handles only linear constraints, unless the writer ``'cplex'`` is used. In the latter case the extended `CPLEX LP format <https://www.ibm.com/support/knowledgecenter/SSSA5P_12.10.0/ ilog.odms.cplex.help/CPLEX/FileFormats/topics/LP.html>`_ is used instead. * ``'.mps'`` -- `MPS format <http://docs.mosek.com/6.0/pyapi/node021.html>`_. As the writer, you need to choose one of ``'cplex'``, ``'gurobi'`` or ``'mosek'``. * ``'.opf'`` -- `OPF format <http://docs.mosek.com/6.0/pyapi/node023.html>`_. As the writer, you need to choose ``'mosek'``. * ``'.dat-s'`` -- `Sparse SDPA format <http://sdpa.indsys.chuo-u.ac.jp/sdpa/download.html#sdpa>`_. This format is suitable for semidefinite programs. Second order cone constraints are stored as semidefinite constraints on an *arrow shaped* matrix. :param str writer: The default ``'picos'`` denotes PICOS' internal writer, which can export to *LP*, *CBF*, and *Sparse SDPA* formats. If CPLEX, Gurobi or MOSEK is installed, you can choose ``'cplex'``, ``'gurobi'``, or ``'mosek'``, respectively, to make use of that solver's export function and get access to more formats. .. warning:: For problems involving a symmetric matrix variable :math:`X` (typically, semidefinite programs), the expressions involving :math:`X` are stored in PICOS as a function of :math:`svec(X)`, the symmetric vectorized form of :math:`X` (see `Dattorro, ch.2.2.2.1 <http://meboo.convexoptimization.com/Meboo.html>`_), and are also exported in that form. As a result, using an external solver on a problem description file exported by PICOS will also yield a solution in this symmetric vectorized form. The CBF writer tries to write symmetric variables :math:`X` in the section ``PSDVAR`` of the .cbf file. However, this is possible only if the constraint :math:`X \succeq 0` appears in the problem, and no other LMI involves :math:`X`. If these two conditions are not satisfied, then the symmetric vectorization of :math:`X` is used as a (free) variable of the section ``VAR`` in the .cbf file, as explained in the previous paragraph. .. warning:: This function is severly outdated and may fail or not function as advertised. """ # HACK: This method abuses the internal problem representation of CVXOPT. # TODO: Add a proper method that transforms the problem into the canonical # form required here, and, if applicable, make also CVXOPT use it. from .strategy import NoStrategyFound # We first prepare the problem so it becomes exportable COMMERCIAL_WRITERS = ['cplex', 'mosek', 'gurobi'] if writer in COMMERCIAL_WRITERS: P = picos_problem.prepared(solver=writer) else: if picos_problem.continuous: # This should ensure that all quadratics have been cast as SOC # constraints. P = picos_problem.prepared(solver='cvxopt', assume_conic=True) else: try: P = picos_problem.prepared(solver=None) except NoStrategyFound as error: # FIXME: This will typically happen for integer linear programs # if no IP solver is available. raise RuntimeError("You try to export a problem for which no " "solver is available") from error assert 'Linear' in P.type, "only LINEAR integer problems can be " \ "exported with the default writer" # TODO: Need proper way to detect exponential cone constraints. This catches # only the Geometric Programs. if P.numberLSEConstraints: raise NotImplementedError("It is not possible (yet) to export GPs or " "problems with Exponential Cone Constraints") # automatic extension recognition if not (any(filename.endswith(ext) for ext in (".mps", ".opf", ".cbf", ".ptf", ".lp", ".dat-s"))): if writer == "gurobi": if (P.numberConeConstraints + P.numberQuadConstraints) == 0: filename += ".lp" else: filename += ".mps" elif writer == "cplex": filename += ".lp" elif writer == "mosek": if (P.numberConeConstraints + P.numberQuadConstraints + P.numberSDPConstraints) == 0: filename += ".lp" elif P.numberQuadConstraints == 0: filename += ".cbf" else: filename += ".mps" elif writer == "picos": assert not P.numberQuadConstraints, \ "Expected no quadratic constraints after call to 'prepared'." if (P.numberConeConstraints + P.numberSDPConstraints) == 0: filename += ".lp" elif P.numberConeConstraints == 0: filename += ".dat-s" else: filename += ".cbf" else: raise Exception("unexpected writer") if writer == "cplex": cpl = CPLEXSolver(P) cpl._load_problem() cpl.int.write(filename) elif writer == "mosek": if filename.endswith(".cbf"): # This ensures that all quadratics are converted to SOC P = picos_problem.prepared(solver='cvxopt') msk = MOSEKSolver(P) msk._load_problem() msk.int.writedata(filename) elif writer == "gurobi": grb = GurobiSolver(P) grb._load_problem() grb.int.write(filename) elif writer == "picos": if filename[-3:] == ".lp": _write_lp(P, filename) elif filename[-6:] == ".dat-s": _write_sdpa(P, filename) elif filename[-4:] == ".cbf": _write_cbf(P, filename) else: raise Exception("unexpected file extension") else: raise Exception("unknown writer")
def _write_lp(P, filename): """Write the problem to a file in LP format.""" # add extension if filename[-3:] != ".lp": filename += ".lp" # check lp compatibility if ( P.numberConeConstraints + P.numberQuadConstraints + P.numberLSEConstraints + P.numberSDPConstraints ) > 0: raise Exception("the picos LP writer only accepts (MI)LP") # open file f = open(filename, "w") f.write("\\* file " + filename + " generated by picos*\\\n") # HACK: This abuses the internal problem representation of CVXOPT. if P.continuous: localCvxoptInstance = CVXOPTSolver(P) else: localCvxoptInstance = CVXOPTSolver(P.continuous_relaxation()) localCvxoptInstance.import_variable_bounds = False localCvxoptInstance._load_problem() cvxoptVars = localCvxoptInstance.internal_problem() # variable names varnames = {} i = 0 for name, v in P.variables.items(): # full vectorization is used, name variables as x, x[j] or x[j,k] if isinstance(v._vec, FullVectorization): for ind in range(v.dim): if v.size == (1, 1): varnames[i] = name elif v.size[1] == 1: varnames[i] = name + "(" + str(ind) + ")" else: k, j = divmod(ind, v.size[0]) varnames[i] = name + "(" + str(j) + "," + str(k) + ")" varnames[i] = varnames[i].replace("[", "(") varnames[i] = varnames[i].replace("]", ")") i += 1 else: for ind in range(v.dim): varnames[i] = 'vec_' + name + "(" + str(ind) + ")" i += 1 # affexpr writer def affexp_writer(constraint_name, indices, coefs): s = "" s += constraint_name s += " : " start = True for (i, v) in zip(indices, coefs): if v > 0 and not (start): s += "+ " s += "%.12g" % v s += " " s += varnames[i] # not the first term anymore start = False if not (coefs): s += "0.0 " s += varnames[0] return s print("writing problem in " + filename + "...") # objective if P.objective.direction == "max": f.write("Maximize\n") # max handled directly cvxoptVars["c"] = -cvxoptVars["c"] else: f.write("Minimize\n") I = cvxopt.sparse(cvxoptVars["c"]).I V = cvxopt.sparse(cvxoptVars["c"]).V f.write(affexp_writer("obj", I, V)) f.write("\n") f.write("Subject To\n") bounds = {} # equality constraints: Ai, Aj, Av = (cvxoptVars["A"].I, cvxoptVars["A"].J, cvxoptVars["A"].V) ijvs = sorted(zip(Ai, Aj, Av)) del Ai, Aj, Av itojv = {} lasti = -1 for (i, j, v) in ijvs: if i == lasti: itojv[i].append((j, v)) else: lasti = i itojv[i] = [(j, v)] ieq = 0 for i, jv in itojv.items(): J = [jvk[0] for jvk in jv] V = [jvk[1] for jvk in jv] if len(J) == 1: # fixed variable b = cvxoptVars["b"][i] / V[0] bounds[J[0]] = (b, b) else: # affine equality b = cvxoptVars["b"][i] f.write(affexp_writer("eq" + str(ieq), J, V)) f.write(" = ") f.write("%.12g" % b) f.write("\n") ieq += 1 # inequality constraints: Gli, Glj, Glv = (cvxoptVars["Gl"].I, cvxoptVars["Gl"].J, cvxoptVars["Gl"].V) ijvs = sorted(zip(Gli, Glj, Glv)) del Gli, Glj, Glv itojv = {} lasti = -1 for (i, j, v) in ijvs: if i == lasti: itojv[i].append((j, v)) else: lasti = i itojv[i] = [(j, v)] iaff = 0 for i, jv in itojv.items(): J = [jvk[0] for jvk in jv] V = [jvk[1] for jvk in jv] b = cvxoptVars["hl"][i] f.write(affexp_writer("in" + str(iaff), J, V)) f.write(" <= ") f.write("%.12g" % b) f.write("\n") iaff += 1 # variable bounds # retrieve as a dictionary {index -> (lo,up)} i_var = 0 for var in P.variables.values(): for ind, lo in var.bound_dicts[0].items(): (current_lo, current_up) = bounds.get( i_var + ind, (-INFINITY, INFINITY)) new_lo = max(lo, current_lo) bounds[i_var + ind] = (new_lo, current_up) for ind, up in var.bound_dicts[1].items(): (current_lo, current_up) = bounds.get( i_var + ind, (-INFINITY, INFINITY)) new_up = min(up, current_up) bounds[i_var + ind] = (current_lo, new_up) i_var += var.dim f.write("Bounds\n") for i in range(P.numberOfVars): if i in bounds: bl, bu = bounds[i] else: bl, bu = -INFINITY, INFINITY if bl == -INFINITY and bu == INFINITY: f.write(varnames[i] + " free") elif bl == bu: f.write(varnames[i] + (" = %.12g" % bl)) elif bl < bu: if bl == -INFINITY: f.write("-inf <= ") else: f.write("%.12g" % bl) f.write(" <= ") f.write(varnames[i]) if bu == INFINITY: f.write("<= +inf") else: f.write(" <= ") f.write("%.12g" % bu) f.write("\n") # general integers f.write("Generals\n") i_var = 0 for name, v in P.variables.items(): if isinstance(v, IntegerVariable): for ind in range(v.dim): f.write(varnames[i_var + ind] + "\n") i_var += v.dim # binary variables f.write("Binaries\n") i_var = 0 for name, v in P.variables.items(): if isinstance(v, BinaryVariable): for ind in range(v.dim): f.write(varnames[i_var + ind] + "\n") i_var += v.dim f.write("End\n") print("done.") f.close() def _write_sdpa(P, filename): """Write the problem to a file in Sparse SDPA format.""" # HACK: This abuses the internal problem representation of CVXOPT. localCvxoptInstance = CVXOPTSolver(P) localCvxoptInstance._load_problem() cvxoptVars = localCvxoptInstance.internal_problem() dims = {} dims["s"] = [int(numpy.sqrt(Gsi.size[0])) for Gsi in cvxoptVars["Gs"]] dims["l"] = cvxoptVars["Gl"].size[0] dims["q"] = [Gqi.size[0] for Gqi in cvxoptVars["Gq"]] G = cvxoptVars["Gl"] h = cvxoptVars["hl"] # handle the equalities as 2 ineq if cvxoptVars["A"].size[0] > 0: G = cvxopt.sparse([G, cvxoptVars["A"]]) G = cvxopt.sparse([G, -cvxoptVars["A"]]) h = cvxopt.matrix([h, cvxoptVars["b"]]) h = cvxopt.matrix([h, -cvxoptVars["b"]]) dims["l"] += 2 * cvxoptVars["A"].size[0] for i in range(len(dims["q"])): G = cvxopt.sparse([G, cvxoptVars["Gq"][i]]) h = cvxopt.matrix([h, cvxoptVars["hq"][i]]) for i in range(len(dims["s"])): G = cvxopt.sparse([G, cvxoptVars["Gs"][i]]) h = cvxopt.matrix([h, cvxoptVars["hs"][i]]) # Remove the lines in A and b corresponding to 0==0 JP = list(set(cvxoptVars["A"].I)) IP = range(len(JP)) VP = [1] * len(JP) # is there a constraint of the form 0==a(a not 0) ? if any([b for (i, b) in enumerate(cvxoptVars["b"]) if i not in JP]): raise Exception("infeasible constraint of the form 0=a") from cvxopt import sparse, spmatrix PP = spmatrix(VP, IP, JP, (len(IP), cvxoptVars["A"].size[0])) cvxoptVars["A"] = PP * cvxoptVars["A"] cvxoptVars["b"] = PP * cvxoptVars["b"] c = cvxoptVars["c"] # ------------------------------------------------------------# # make A,B,and blockstruct. # # This code is a modification of the conelp function in SMCP # # ------------------------------------------------------------# Nl = dims["l"] Nq = dims["q"] Ns = dims["s"] if not Nl: Nl = 0 P_m = G.size[1] P_b = -c P_blockstruct = [] if Nl: P_blockstruct.append(-Nl) for i in Nq: P_blockstruct.append(i) for i in Ns: P_blockstruct.append(i) # write data # add extension if filename[-6:] != ".dat-s": filename += ".dat-s" # open file f = open(filename, "w") f.write('"file ' + filename + ' generated by picos"\n') if P.options.verbosity >= 1: print("writing problem in " + filename + "...") f.write(str(P.numberOfVars) + " = number of vars\n") f.write(str(len(P_blockstruct)) + " = number of blocs\n") # bloc structure f.write(str(P_blockstruct).replace("[", "(").replace("]", ")")) f.write(" = BlocStructure\n") # c vector (objective) f.write(str(list(-P_b)).replace("[", "{").replace("]", "}")) f.write("\n") # coefs for k in range(P_m + 1): if k != 0: v = sparse(G[:, k - 1]) else: v = +sparse(h) ptr = 0 block = 0 # lin. constraints if Nl: u = v[:Nl] for i, j, value in zip(u.I, u.I, u.V): f.write( "{0}\t{1}\t{2}\t{3}\t{4}\n".format( k, block + 1, j + 1, i + 1, -value ) ) ptr += Nl block += 1 # SOC constraints for nq in Nq: u0 = v[ptr] u1 = v[ptr + 1:ptr + nq] tmp = spmatrix( u1.V, [nq - 1 for j in range(len(u1))], u1.I, (nq, nq) ) if not u0 == 0.0: tmp += spmatrix(u0, range(nq), range(nq), (nq, nq)) for i, j, value in zip(tmp.I, tmp.J, tmp.V): f.write( "{0}\t{1}\t{2}\t{3}\t{4}\n".format( k, block + 1, j + 1, i + 1, -value ) ) ptr += nq block += 1 # SDP constraints for ns in Ns: u = v[ptr:ptr + ns ** 2] for index_k, index in enumerate(u.I): j, i = divmod(index, ns) if j <= i: f.write( "{0}\t{1}\t{2}\t{3}\t{4}\n".format( k, block + 1, j + 1, i + 1, -u.V[index_k] ) ) ptr += ns ** 2 block += 1 f.close() # This is an ugly function from a former version of picos # It separates a linear constraint between 'plain' vars and matrix 'bar' # variables J and V denote the sparse indices/values of the constraints for the # whole (s-)vectorized vector (without offset) def _separate_linear_cons_plain_bar_vars(J, V, idx_sdp_vars): # sparse values of the constraint for 'plain' variables jj = [] vv = [] # sparse values of the constraint for the next svec bar variable js = [] vs = [] mats = [] offset = 0 if idx_sdp_vars: idxsdpvars = [ti for ti in idx_sdp_vars] nextsdp = idxsdpvars.pop() else: return J, V, [] for (j, v) in zip(J, V): if j < nextsdp[0]: jj.append(j - offset) vv.append(v) elif j < nextsdp[1]: js.append(j - nextsdp[0]) vs.append(v) else: while j >= nextsdp[1]: mats.append( devectorize( cvxopt.spmatrix( vs, js, [0] * len(js), (nextsdp[1] - nextsdp[0], 1) )).T) js = [] vs = [] offset += (nextsdp[1] - nextsdp[0]) try: nextsdp = idxsdpvars.pop() except IndexError: nextsdp = (float('inf'), float('inf')) if j < nextsdp[0]: jj.append(j - offset) vv.append(v) elif j < nextsdp[1]: js.append(j - nextsdp[0]) vs.append(v) while len(mats) < len(idx_sdp_vars): mats.append( devectorize( cvxopt.spmatrix( vs, js, [0] * len(js), (nextsdp[1] - nextsdp[0], 1) )).T) js = [] vs = [] nextsdp = (0, 1) # doesnt matter, it will be an empt matrix anyway return jj, vv, mats
[docs]def devectorize(vec): """Create a matrix from a symmetric vectorization.""" from ..expressions.vectorizations import SymmetricVectorization v = vec.size[0] n = int((1 + 8 * v) ** 0.5 - 1) // 2 return SymmetricVectorization((n, n)).devectorize(vec)
def _write_cbf(P, filename, uptri=False): """Write the problem to a file in Sparse SDPA format. :param bool uptri: Whether upper triangular elements of symmetric matrices are specified. """ # write data # add extension if filename[-4:] != ".cbf": filename += ".cbf" # parse variables semidef_vars = set() for cons in P.constraints: cs = P.constraints[cons] if isinstance(cs, LMIConstraint): if cs.semidefVar: semidef_vars.add(cs.semidefVar) NUMVAR_SCALAR = int( sum( [var.dim for var in P.variables.values() if var not in semidef_vars] ) ) ind = 0 indices = [] start_indices = {} for v in P.variables.values(): indices.append((ind, ind + v.dim, v)) start_indices[v] = ind ind += v.dim indices = sorted(indices) idxsdpvars = [ (si, ei) for (si, ei, v) in indices[::-1] if v in semidef_vars] # search if some semidef vars are implied in other semidef constraints PSD_not_handled = [] for c in P.constraints.values(): if isinstance(c, LMIConstraint) and c not in semidef_vars: for v in (c.lhs - c.rhs)._coefs: if v in semidef_vars: start_index = start_indices[v] idx = (start_index, start_index + v.dim) if idx in idxsdpvars: PSD_not_handled.append(v) NUMVAR_SCALAR += idx[1] - idx[0] idxsdpvars.remove(idx) barvars = bool(idxsdpvars) # find integer variables, retrieve bounds and put 0-1 bounds on binaries ints = [] ind = 0 varbounds_lo = {} varbounds_up = {} for k, var in P.variables.items(): if isinstance(var, BinaryVariable): for relind, absind in enumerate(range(ind, ind + var.dim)): ints.append(absind) clb = var.bound_dicts[0].get(relind, -INFINITY) cub = var.bound_dicts[1].get(relind, INFINITY) varbounds_lo[absind] = max(0.0, clb) varbounds_up[absind] = min(1.0, cub) elif (isinstance(var, IntegerVariable) or isinstance(var, CONTINUOUS_VARTYPES)): for relind, absind in enumerate(range(ind, ind + var.dim)): if isinstance(var, IntegerVariable): ints.append(absind) clb = var.bound_dicts[0].get(relind, -INFINITY) cub = var.bound_dicts[1].get(relind, INFINITY) varbounds_lo[absind] = clb varbounds_up[absind] = cub else: raise Exception("variable type not handled by _write_cbf()") ind += var.dim if barvars: ints, _, mats = _separate_linear_cons_plain_bar_vars( ints, [0.0] * len(ints), idxsdpvars ) if any([bool(mat) for mat in mats]): raise Exception( "semidef vars with integer elements are not supported" ) # open file f = open(filename, "w") f.write("#file " + filename + " generated by picos\n") print("writing problem in " + filename + "...") f.write("VER\n") f.write("1\n\n") f.write("OBJSENSE\n") if P.objective.direction == "max": f.write("MAX\n\n") else: f.write("MIN\n\n") # VARIABLEs if barvars: f.write("PSDVAR\n") f.write(str(len(idxsdpvars)) + "\n") for si, ei in idxsdpvars: ni = int(((8 * (ei - si) + 1) ** 0.5 - 1) / 2.0) f.write(str(ni) + "\n") f.write("\n") # bounds cones = [] conecons = [] Acoord = [] Bcoord = [] iaff = 0 offset = 0 for si, ei, v in indices: if v in semidef_vars and not (v in PSD_not_handled): offset += ei - si else: if all(varbounds_lo[ind] == 0 for ind in range(si, ei)): cones.append(("L+", ei - si)) elif all(varbounds_up[ind] == 0 for ind in range(si, ei)): cones.append(("L-", ei - si)) else: cones.append(("F", ei - si)) if any(varbounds_lo[ind] != 0 for ind in range(si, ei)): for ind in range(si, ei): l = varbounds_lo[ind] if l - 1 > -INFINITY: Acoord.append((iaff, ind - offset, 1.0)) Bcoord.append((iaff, -l)) iaff += 1 if any(varbounds_up[ind] != 0 for ind in range(si, ei)): for ind in range(si, ei): u = varbounds_up[ind] if u + 1 < INFINITY: Acoord.append((iaff, ind - offset, -1.0)) Bcoord.append((iaff, u)) iaff += 1 if iaff: conecons.append(("L+", iaff)) f.write("VAR\n") f.write(str(NUMVAR_SCALAR) + " " + str(len(cones)) + "\n") for tp, n in cones: f.write(tp + " " + str(n) + "\n") f.write("\n") # integers if ints: f.write("INT\n") f.write(str(len(ints)) + "\n") for i in ints: f.write(str(i) + "\n") f.write("\n") # constraints psdcons = [] isdp = 0 Fcoord = [] Hcoord = [] Dcoord = [] ObjAcoord = [] ObjBcoord = [] ObjFcoord = [] # dummy constraint for the objective dummy_cons = P.objective.function >= 0 setattr(dummy_cons, "dummycon", None) for cons in chain((dummy_cons,), P.constraints.values()): if isinstance(cons, LMIConstraint): v = cons.semidefVar if v is not None and v not in PSD_not_handled: continue # get sparse indices if isinstance(cons, AffineConstraint): expcone = cons.lhs - cons.rhs if hasattr(cons, "dummycon"): conetype = "0" # Dummy type for the objective function. elif cons.is_equality(): conetype = "L=" elif cons.is_increasing(): conetype = "L-" elif cons.is_decreasing(): conetype = "L+" else: assert False, "Unexpected constraint relation." elif isinstance(cons, SOCConstraint): expcone = (cons.ub) // (cons.ne[:]) conetype = "Q" elif isinstance(cons, RSOCConstraint): expcone = (cons.ub1) // (0.5 * cons.ub2) // (cons.ne[:]) conetype = "QR" elif isinstance(cons, LMIConstraint): if cons.is_increasing(): expcone = cons.rhs - cons.lhs conetype = None elif cons.is_decreasing(): expcone = cons.lhs - cons.rhs conetype = None else: assert False, "Unexpected constraint relation." else: assert False, "Unexpected constraint type." ijv = [] for var, fact in expcone._coefs.items(): if not isinstance(fact, cvxopt.base.spmatrix): fact = cvxopt.sparse(fact) sj = start_indices[var] ijv.extend(zip(fact.I, fact.J + sj, fact.V)) ijvs = sorted(ijv) itojv = {} lasti = -1 for (i, j, v) in ijvs: if i == lasti: itojv[i].append((j, v)) else: lasti = i itojv[i] = [(j, v)] if conetype: if conetype != "0": dim = expcone.size[0] * expcone.size[1] conecons.append((conetype, dim)) else: dim = expcone.size[0] psdcons.append(dim) if conetype: for i, jv in itojv.items(): J = [jvk[0] for jvk in jv] V = [jvk[1] for jvk in jv] J, V, mats = _separate_linear_cons_plain_bar_vars( J, V, idxsdpvars) for j, v in zip(J, V): if conetype != "0": Acoord.append((iaff + i, j, v)) else: ObjAcoord.append((j, v)) for k, mat in enumerate(mats): for row, col, v in zip(mat.I, mat.J, mat.V): if conetype != "0": Fcoord.append((iaff + i, k, row, col, v)) else: ObjFcoord.append((k, row, col, v)) if uptri and row != col: if conetype != "0": Fcoord.append((iaff + i, k, col, row, v)) else: ObjFcoord.append((k, col, row, v)) constant = expcone._const if not (constant is None): constant = cvxopt.sparse(constant) for i, v in zip(constant.I, constant.V): if conetype != "0": Bcoord.append((iaff + i, v)) else: ObjBcoord.append(v) else: for i, jv in itojv.items(): col, row = divmod(i, dim) if not (uptri) and row < col: continue J = [jvk[0] for jvk in jv] V = [jvk[1] for jvk in jv] J, V, mats = _separate_linear_cons_plain_bar_vars( J, V, idxsdpvars) if any([bool(m) for m in mats]): raise Exception("SDP cons should not depend on PSD var") for j, v in zip(J, V): Hcoord.append((isdp, j, row, col, v)) constant = expcone._const if not (constant is None): constant = cvxopt.sparse(constant) for i, v in zip(constant.I, constant.V): col, row = divmod(i, dim) if row < col: continue Dcoord.append((isdp, row, col, v)) if conetype: if conetype != "0": iaff += dim else: isdp += 1 if iaff > 0: f.write("CON\n") f.write(str(iaff) + " " + str(len(conecons)) + "\n") for tp, n in conecons: f.write(tp + " " + str(n)) f.write("\n") f.write("\n") if isdp > 0: f.write("PSDCON\n") f.write(str(isdp) + "\n") for n in psdcons: f.write(str(n) + "\n") f.write("\n") if ObjFcoord: f.write("OBJFCOORD\n") f.write(str(len(ObjFcoord)) + "\n") for (k, row, col, v) in ObjFcoord: f.write("{0} {1} {2} {3}\n".format(k, row, col, v)) f.write("\n") if ObjAcoord: f.write("OBJACOORD\n") f.write(str(len(ObjAcoord)) + "\n") for (j, v) in ObjAcoord: f.write("{0} {1}\n".format(j, v)) f.write("\n") if ObjBcoord: f.write("OBJBCOORD\n") v = ObjBcoord[0] f.write("{0}\n".format(v)) f.write("\n") if Fcoord: f.write("FCOORD\n") f.write(str(len(Fcoord)) + "\n") for (i, k, row, col, v) in Fcoord: f.write("{0} {1} {2} {3} {4}\n".format(i, k, row, col, v)) f.write("\n") if Acoord: f.write("ACOORD\n") f.write(str(len(Acoord)) + "\n") for (i, j, v) in Acoord: f.write("{0} {1} {2}\n".format(i, j, v)) f.write("\n") if Bcoord: f.write("BCOORD\n") f.write(str(len(Bcoord)) + "\n") for (i, v) in Bcoord: f.write("{0} {1}\n".format(i, v)) f.write("\n") if Hcoord: f.write("HCOORD\n") f.write(str(len(Hcoord)) + "\n") for (i, j, row, col, v) in Hcoord: f.write("{0} {1} {2} {3} {4}\n".format(i, j, row, col, v)) f.write("\n") if Dcoord: f.write("DCOORD\n") f.write(str(len(Dcoord)) + "\n") for (i, row, col, v) in Dcoord: f.write("{0} {1} {2} {3}\n".format(i, row, col, v)) f.write("\n") print("done.") f.close() # -------------------------------------- __all__ = api_end(_API_START, globals())