def t_qps_pypower(quiet=False): """Tests of C{qps_pypower} QP solvers. @author: Ray Zimmerman (PSERC Cornell) """ algs = [200, 250, 400, 500, 600, 700] names = ['PIPS', 'sc-PIPS', 'IPOPT', 'CPLEX', 'MOSEK', 'Gurobi'] check = [None, None, 'ipopt', 'cplex', 'mosek', 'gurobipy'] n = 36 t_begin(n * len(algs), quiet) for k in range(len(algs)): if check[k] is not None and not have_fcn(check[k]): t_skip(n, '%s not installed' % names[k]) else: opt = {'verbose': 0, 'alg': algs[k]} if names[k] == 'PIPS' or names[k] == 'sc-PIPS': opt['pips_opt'] = {} opt['pips_opt']['comptol'] = 1e-8 if names[k] == 'CPLEX': # alg = 0 ## default uses barrier method with NaN bug in lower lim multipliers alg = 2 ## use dual simplex ppopt = ppoption(CPLEX_LPMETHOD = alg, CPLEX_QPMETHOD = min([4, alg])) opt['cplex_opt'] = cplex_options([], ppopt) if names[k] == 'MOSEK': # alg = 5 ## use dual simplex ppopt = ppoption() # ppopt = ppoption(ppopt, MOSEK_LP_ALG = alg) ppopt = ppoption(ppopt, MOSEK_GAP_TOL=1e-9) opt['mosek_opt'] = mosek_options([], ppopt) t = '%s - 3-d LP : ' % names[k] ## example from 'doc linprog' c = array([-5, -4, -6], float) A = sparse([[1, -1, 1], [3, 2, 4], [3, 2, 0]], dtype=float) l = None u = array([20, 42, 30], float) xmin = array([0, 0, 0], float) x0 = None x, f, s, _, lam = qps_pypower(None, c, A, l, u, xmin, None, None, opt) t_is(s, 1, 12, [t, 'success']) t_is(x, [0, 15, 3], 6, [t, 'x']) t_is(f, -78, 6, [t, 'f']) t_is(lam['mu_l'], [0, 0, 0], 13, [t, 'lam.mu_l']) t_is(lam['mu_u'], [0, 1.5, 0.5], 9, [t, 'lam.mu_u']) t_is(lam['lower'], [1, 0, 0], 9, [t, 'lam.lower']) t_is(lam['upper'], zeros(shape(x)), 13, [t, 'lam.upper']) t = '%s - unconstrained 3-d quadratic : ' % names[k] ## from http://www.akiti.ca/QuadProgEx0Constr.html H = sparse([ [ 5, -2, -1], [-2, 4, 3], [-1, 3, 5] ], dtype=float) c = array([2, -35, -47], float) x0 = array([0, 0, 0], float) x, f, s, _, lam = qps_pypower(H, c, opt=opt) t_is(s, 1, 12, [t, 'success']) t_is(x, [3, 5, 7], 8, [t, 'x']) t_is(f, -249, 13, [t, 'f']) t_ok(len(lam['mu_l']) == 0, [t, 'lam.mu_l']) t_ok(len(lam['mu_u']) == 0, [t, 'lam.mu_u']) t_is(lam['lower'], zeros(shape(x)), 13, [t, 'lam.lower']) t_is(lam['upper'], zeros(shape(x)), 13, [t, 'lam.upper']) t = '%s - constrained 2-d QP : ' % names[k] ## example from 'doc quadprog' H = sparse([[ 1, -1], [-1, 2]], dtype=float) c = array([-2, -6], float) A = sparse([[ 1, 1], [-1, 2], [ 2, 1]], dtype=float) l = None u = array([2, 2, 3], float) xmin = array([0, 0]) x0 = None x, f, s, _, lam = qps_pypower(H, c, A, l, u, xmin, None, x0, opt) t_is(s, 1, 12, [t, 'success']) t_is(x, array([2., 4.]) / 3, 7, [t, 'x']) t_is(f, -74. / 9, 6, [t, 'f']) t_is(lam['mu_l'], [0., 0., 0.], 13, [t, 'lam.mu_l']) t_is(lam['mu_u'], array([28., 4., 0.]) / 9, 7, [t, 'lam.mu_u']) t_is(lam['lower'], zeros(shape(x)), 8, [t, 'lam.lower']) t_is(lam['upper'], zeros(shape(x)), 13, [t, 'lam.upper']) t = '%s - constrained 4-d QP : ' % names[k] ## from http://www.jmu.edu/docs/sasdoc/sashtml/iml/chap8/sect12.htm H = sparse([[1003.1, 4.3, 6.3, 5.9], [4.3, 2.2, 2.1, 3.9], [6.3, 2.1, 3.5, 4.8], [5.9, 3.9, 4.8, 10.0]]) c = zeros(4) A = sparse([[ 1, 1, 1, 1], [0.17, 0.11, 0.10, 0.18]]) l = array([1, 0.10]) u = array([1, Inf]) xmin = zeros(4) x0 = array([1, 0, 0, 1], float) x, f, s, _, lam = qps_pypower(H, c, A, l, u, xmin, None, x0, opt) t_is(s, 1, 12, [t, 'success']) t_is(x, array([0, 2.8, 0.2, 0]) / 3, 5, [t, 'x']) t_is(f, 3.29 / 3, 6, [t, 'f']) t_is(lam['mu_l'], array([6.58, 0]) / 3, 6, [t, 'lam.mu_l']) t_is(lam['mu_u'], [0, 0], 13, [t, 'lam.mu_u']) t_is(lam['lower'], [2.24, 0, 0, 1.7667], 4, [t, 'lam.lower']) t_is(lam['upper'], zeros(shape(x)), 13, [t, 'lam.upper']) t = '%s - (dict) constrained 4-d QP : ' % names[k] p = {'H': H, 'A': A, 'l': l, 'u': u, 'xmin': xmin, 'x0': x0, 'opt': opt} x, f, s, _, lam = qps_pypower(p) t_is(s, 1, 12, [t, 'success']) t_is(x, array([0, 2.8, 0.2, 0]) / 3, 5, [t, 'x']) t_is(f, 3.29 / 3, 6, [t, 'f']) t_is(lam['mu_l'], array([6.58, 0]) / 3, 6, [t, 'lam.mu_l']) t_is(lam['mu_u'], [0, 0], 13, [t, 'lam.mu_u']) t_is(lam['lower'], [2.24, 0, 0, 1.7667], 4, [t, 'lam.lower']) t_is(lam['upper'], zeros(shape(x)), 13, [t, 'lam.upper']) t = '%s - infeasible LP : ' % names[k] p = {'A': sparse([1, 1]), 'c': array([1, 1]), 'u': array([-1]), 'xmin': array([0, 0]), 'opt': opt} x, f, s, _, lam = qps_pypower(p) t_ok(s <= 0, [t, 'no success']) t_end()
def dcopf_solver(om, ppopt, out_opt=None): """Solves a DC optimal power flow. Inputs are an OPF model object, a PYPOWER options dict and a dict containing fields (can be empty) for each of the desired optional output fields. Outputs are a C{results} dict, C{success} flag and C{raw} output dict. C{results} is a PYPOWER case dict (ppc) with the usual baseMVA, bus branch, gen, gencost fields, along with the following additional fields: - C{order} see 'help ext2int' for details of this field - C{x} final value of optimization variables (internal order) - C{f} final objective function value - C{mu} shadow prices on ... - C{var} - C{l} lower bounds on variables - C{u} upper bounds on variables - C{lin} - C{l} lower bounds on linear constraints - C{u} upper bounds on linear constraints - C{g} (optional) constraint values - C{dg} (optional) constraint 1st derivatives - C{df} (optional) obj fun 1st derivatives (not yet implemented) - C{d2f} (optional) obj fun 2nd derivatives (not yet implemented) C{success} is C{True} if solver converged successfully, C{False} otherwise. C{raw} is a raw output dict in form returned by MINOS - C{xr} final value of optimization variables - C{pimul} constraint multipliers - C{info} solver specific termination code - C{output} solver specific output information @see: L{opf}, L{qps_pypower} @author: Ray Zimmerman (PSERC Cornell) @author: Carlos E. Murillo-Sanchez (PSERC Cornell & Universidad Autonoma de Manizales) @author: Richard Lincoln """ if out_opt is None: out_opt = {} ## options verbose = ppopt['VERBOSE'] alg = ppopt['OPF_ALG_DC'] if alg == 0: if have_fcn('cplex'): ## use CPLEX by default, if available alg = 500 elif have_fcn('mosek'): ## if not, then MOSEK, if available alg = 600 elif have_fcn('gurobi'): ## if not, then Gurobi, if available alg = 700 else: ## otherwise PIPS alg = 200 ## unpack data ppc = om.get_ppc() baseMVA, bus, gen, branch, gencost = \ ppc["baseMVA"], ppc["bus"], ppc["gen"], ppc["branch"], ppc["gencost"] cp = om.get_cost_params() N, H, Cw = cp["N"], cp["H"], cp["Cw"] fparm = array(c_[cp["dd"], cp["rh"], cp["kk"], cp["mm"]]) Bf = om.userdata('Bf') Pfinj = om.userdata('Pfinj') vv, ll, _, _ = om.get_idx() ## problem dimensions ipol = find(gencost[:, MODEL] == POLYNOMIAL) ## polynomial costs ipwl = find(gencost[:, MODEL] == PW_LINEAR) ## piece-wise linear costs nb = bus.shape[0] ## number of buses nl = branch.shape[0] ## number of branches nw = N.shape[0] ## number of general cost vars, w ny = om.getN('var', 'y') ## number of piece-wise linear costs nxyz = om.getN('var') ## total number of control vars of all types ## linear constraints & variable bounds A, l, u = om.linear_constraints() x0, xmin, xmax = om.getv() ## set up objective function of the form: f = 1/2 * X'*HH*X + CC'*X ## where X = [x;y;z]. First set up as quadratic function of w, ## f = 1/2 * w'*HHw*w + CCw'*w, where w = diag(M) * (N*X - Rhat). We ## will be building on the (optionally present) user supplied parameters. ## piece-wise linear costs any_pwl = int(ny > 0) if any_pwl: # Sum of y vars. Npwl = sparse( (ones(ny), (zeros(ny), arange(vv["i1"]["y"], vv["iN"]["y"]))), (1, nxyz)) Hpwl = sparse((1, 1)) Cpwl = array([1]) fparm_pwl = array([[1, 0, 0, 1]]) else: Npwl = None #zeros((0, nxyz)) Hpwl = None #array([]) Cpwl = array([]) fparm_pwl = zeros((0, 4)) ## quadratic costs npol = len(ipol) if any(find(gencost[ipol, NCOST] > 3)): stderr.write('DC opf cannot handle polynomial costs with higher ' 'than quadratic order.\n') iqdr = find(gencost[ipol, NCOST] == 3) ilin = find(gencost[ipol, NCOST] == 2) polycf = zeros((npol, 3)) ## quadratic coeffs for Pg if len(iqdr) > 0: polycf[iqdr, :] = gencost[ipol[iqdr], COST:COST + 3] if npol: polycf[ilin, 1:3] = gencost[ipol[ilin], COST:COST + 2] polycf = dot(polycf, diag([baseMVA**2, baseMVA, 1])) ## convert to p.u. if npol: Npol = sparse((ones(npol), (arange(npol), vv["i1"]["Pg"] + ipol)), (npol, nxyz)) # Pg vars Hpol = sparse((2 * polycf[:, 0], (arange(npol), arange(npol))), (npol, npol)) else: Npol = None Hpol = None Cpol = polycf[:, 1] fparm_pol = ones((npol, 1)) * array([[1, 0, 0, 1]]) ## combine with user costs NN = vstack( [n for n in [Npwl, Npol, N] if n is not None and n.shape[0] > 0], "csr") # FIXME: Zero dimension sparse matrices. if (Hpwl is not None) and any_pwl and (npol + nw): Hpwl = hstack([Hpwl, sparse((any_pwl, npol + nw))]) if Hpol is not None: if any_pwl and npol: Hpol = hstack([sparse((npol, any_pwl)), Hpol]) if npol and nw: Hpol = hstack([Hpol, sparse((npol, nw))]) if (H is not None) and nw and (any_pwl + npol): H = hstack([sparse((nw, any_pwl + npol)), H]) HHw = vstack( [h for h in [Hpwl, Hpol, H] if h is not None and h.shape[0] > 0], "csr") CCw = r_[Cpwl, Cpol, Cw] ffparm = r_[fparm_pwl, fparm_pol, fparm] ## transform quadratic coefficients for w into coefficients for X nnw = any_pwl + npol + nw M = sparse((ffparm[:, 3], (range(nnw), range(nnw)))) MR = M * ffparm[:, 1] HMR = HHw * MR MN = M * NN HH = MN.T * HHw * MN CC = MN.T * (CCw - HMR) C0 = 0.5 * dot(MR, HMR) + sum(polycf[:, 2]) # Constant term of cost. ## set up input for QP solver opt = {'alg': alg, 'verbose': verbose} if (alg == 200) or (alg == 250): ## try to select an interior initial point Varefs = bus[bus[:, BUS_TYPE] == REF, VA] * (pi / 180.0) lb, ub = xmin.copy(), xmax.copy() lb[xmin == -Inf] = -1e10 ## replace Inf with numerical proxies ub[xmax == Inf] = 1e10 x0 = (lb + ub) / 2 # angles set to first reference angle x0[vv["i1"]["Va"]:vv["iN"]["Va"]] = Varefs[0] if ny > 0: ipwl = find(gencost[:, MODEL] == PW_LINEAR) # largest y-value in CCV data c = gencost.flatten('F')[sub2ind(gencost.shape, ipwl, NCOST + 2 * gencost[ipwl, NCOST])] x0[vv["i1"]["y"]:vv["iN"]["y"]] = max(c) + 0.1 * abs(max(c)) ## set up options feastol = ppopt['PDIPM_FEASTOL'] gradtol = ppopt['PDIPM_GRADTOL'] comptol = ppopt['PDIPM_COMPTOL'] costtol = ppopt['PDIPM_COSTTOL'] max_it = ppopt['PDIPM_MAX_IT'] max_red = ppopt['SCPDIPM_RED_IT'] if feastol == 0: feastol = ppopt['OPF_VIOLATION'] ## = OPF_VIOLATION by default opt["pips_opt"] = { 'feastol': feastol, 'gradtol': gradtol, 'comptol': comptol, 'costtol': costtol, 'max_it': max_it, 'max_red': max_red, 'cost_mult': 1 } elif alg == 400: opt['ipopt_opt'] = ipopt_options([], ppopt) elif alg == 500: opt['cplex_opt'] = cplex_options([], ppopt) elif alg == 600: opt['mosek_opt'] = mosek_options([], ppopt) elif alg == 700: opt['grb_opt'] = gurobi_options([], ppopt) else: raise ValueError("Unrecognised solver [%d]." % alg) ##----- run opf ----- x, f, info, output, lmbda = \ qps_pypower(HH, CC, A, l, u, xmin, xmax, x0, opt) success = (info == 1) ##----- calculate return values ----- if not any(isnan(x)): ## update solution data Va = x[vv["i1"]["Va"]:vv["iN"]["Va"]] Pg = x[vv["i1"]["Pg"]:vv["iN"]["Pg"]] f = f + C0 ## update voltages & generator outputs bus[:, VA] = Va * 180 / pi gen[:, PG] = Pg * baseMVA ## compute branch flows branch[:, [QF, QT]] = zeros((nl, 2)) branch[:, PF] = (Bf * Va + Pfinj) * baseMVA branch[:, PT] = -branch[:, PF] ## package up results mu_l = lmbda["mu_l"] mu_u = lmbda["mu_u"] muLB = lmbda["lower"] muUB = lmbda["upper"] ## update Lagrange multipliers il = find((branch[:, RATE_A] != 0) & (branch[:, RATE_A] < 1e10)) bus[:, [LAM_P, LAM_Q, MU_VMIN, MU_VMAX]] = zeros((nb, 4)) gen[:, [MU_PMIN, MU_PMAX, MU_QMIN, MU_QMAX]] = zeros((gen.shape[0], 4)) branch[:, [MU_SF, MU_ST]] = zeros((nl, 2)) bus[:, LAM_P] = (mu_u[ll["i1"]["Pmis"]:ll["iN"]["Pmis"]] - mu_l[ll["i1"]["Pmis"]:ll["iN"]["Pmis"]]) / baseMVA branch[il, MU_SF] = mu_u[ll["i1"]["Pf"]:ll["iN"]["Pf"]] / baseMVA branch[il, MU_ST] = mu_u[ll["i1"]["Pt"]:ll["iN"]["Pt"]] / baseMVA gen[:, MU_PMIN] = muLB[vv["i1"]["Pg"]:vv["iN"]["Pg"]] / baseMVA gen[:, MU_PMAX] = muUB[vv["i1"]["Pg"]:vv["iN"]["Pg"]] / baseMVA pimul = r_[mu_l - mu_u, -ones( (ny > 0)), ## dummy entry corresponding to linear cost row in A muLB - muUB] mu = {'var': {'l': muLB, 'u': muUB}, 'lin': {'l': mu_l, 'u': mu_u}} results = deepcopy(ppc) results["bus"], results["branch"], results["gen"], \ results["om"], results["x"], results["mu"], results["f"] = \ bus, branch, gen, om, x, mu, f raw = {'xr': x, 'pimul': pimul, 'info': info, 'output': output} return results, success, raw
def qps_cplex(H, c, A, l, u, xmin, xmax, x0, opt): """Quadratic Program Solver based on CPLEX. A wrapper function providing a PYPOWER standardized interface for using C{cplexqp} or C{cplexlp} to solve the following QP (quadratic programming) problem:: min 1/2 X'*H*x + c'*x x subject to:: l <= A*x <= u (linear constraints) xmin <= x <= xmax (variable bounds) Inputs (all optional except C{H}, C{c}, C{A} and C{l}): - C{H} : matrix (possibly sparse) of quadratic cost coefficients - C{c} : vector of linear cost coefficients - C{A, l, u} : define the optional linear constraints. Default values for the elements of L and U are -Inf and Inf, respectively. - C{xmin, xmax} : optional lower and upper bounds on the C{x} variables, defaults are -Inf and Inf, respectively. - C{x0} : optional starting value of optimization vector C{x} - C{opt} : optional options structure with the following fields, all of which are also optional (default values shown in parentheses) - C{verbose} (0) - controls level of progress output displayed - 0 = no progress output - 1 = some progress output - 2 = verbose progress output - C{cplex_opt} - options dict for CPLEX, value in verbose overrides these options - C{problem} : The inputs can alternatively be supplied in a single C{problem} dict with fields corresponding to the input arguments described above: C{H, c, A, l, u, xmin, xmax, x0, opt} Outputs: - C{x} : solution vector - C{f} : final objective function value - C{exitflag} : CPLEXQP/CPLEXLP exit flag (see C{cplexqp} and C{cplexlp} documentation for details) - C{output} : CPLEXQP/CPLEXLP output dict (see C{cplexqp} and C{cplexlp} documentation for details) - C{lmbda} : dict containing the Langrange and Kuhn-Tucker multipliers on the constraints, with fields: - mu_l - lower (left-hand) limit on linear constraints - mu_u - upper (right-hand) limit on linear constraints - lower - lower bound on optimization variables - upper - upper bound on optimization variables @author: Ray Zimmerman (PSERC Cornell) """ ##----- input argument handling ----- ## gather inputs if isinstance(H, dict): ## problem struct p = H if 'opt' in p: opt = p['opt'] if 'x0' in p: x0 = p['x0'] if 'xmax' in p: xmax = p['xmax'] if 'xmin' in p: xmin = p['xmin'] if 'u' in p: u = p['u'] if 'l' in p: l = p['l'] if 'A' in p: A = p['A'] if 'c' in p: c = p['c'] if 'H' in p: H = p['H'] else: ## individual args assert H is not None assert c is not None assert A is not None assert l is not None if opt is None: opt = {} # if x0 is None: # x0 = array([]) # if xmax is None: # xmax = array([]) # if xmin is None: # xmin = array([]) ## define nx, set default values for missing optional inputs if len(H) == 0 or not any(any(H)): if len(A) == 0 and len(xmin) == 0 and len(xmax) == 0: stderr.write( 'qps_cplex: LP problem must include constraints or variable bounds\n' ) else: if len(A) > 0: nx = shape(A)[1] elif len(xmin) > 0: nx = len(xmin) else: # if len(xmax) > 0 nx = len(xmax) else: nx = shape(H)[0] if len(c) == 0: c = zeros(nx) if len(A) > 0 and (len(l) == 0 or all(l == -Inf)) and \ (len(u) == 0 or all(u == Inf)): A = None ## no limits => no linear constraints nA = shape(A)[0] ## number of original linear constraints if len(u) == 0: ## By default, linear inequalities are ... u = Inf * ones(nA) ## ... unbounded above and ... if len(l) == 0: l = -Inf * ones(nA) ## ... unbounded below. if len(xmin) == 0: ## By default, optimization variables are ... xmin = -Inf * ones(nx) ## ... unbounded below and ... if len(xmax) == 0: xmax = Inf * ones(nx) ## ... unbounded above. if len(x0) == 0: x0 = zeros(nx) ## default options if 'verbose' in opt: verbose = opt['verbose'] else: verbose = 0 #if 'max_it' in opt: # max_it = opt['max_it'] #else: # max_it = 0 ## split up linear constraints ieq = find(abs(u - l) <= EPS) ## equality igt = find(u >= 1e10 & l > -1e10) ## greater than, unbounded above ilt = find(l <= -1e10 & u < 1e10) ## less than, unbounded below ibx = find((abs(u - l) > EPS) & (u < 1e10) & (l > -1e10)) Ae = A[ieq, :] be = u[ieq] Ai = r_[A[ilt, :], -A[igt, :], A[ibx, :] - A[ibx, :]] bi = r_[u[ilt], -l[igt], u[ibx], -l[ibx]] ## grab some dimensions nlt = len(ilt) ## number of upper bounded linear inequalities ngt = len(igt) ## number of lower bounded linear inequalities nbx = len(ibx) ## number of doubly bounded linear inequalities ## set up options struct for CPLEX if 'cplex_opt' in opt: cplex_opt = cplex_options(opt['cplex_opt']) else: cplex_opt = cplex_options cplex = Cplex('null') vstr = cplex.getVersion s, e, tE, m, t = re.compile(vstr, '(\d+\.\d+)\.') vnum = int(t[0][0]) vrb = max([0, verbose - 1]) cplex_opt['barrier']['display'] = vrb cplex_opt['conflict']['display'] = vrb cplex_opt['mip']['display'] = vrb cplex_opt['sifting']['display'] = vrb cplex_opt['simplex']['display'] = vrb cplex_opt['tune']['display'] = vrb if vrb and (vnum > 12.2): cplex_opt['diagnostics'] = 'on' #if max_it: # cplex_opt. ## not sure what to set here if len(Ai) == 0 and len(Ae) == 0: unconstrained = 1 Ae = sparse((1, nx)) be = 0 else: unconstrained = 0 ## call the solver if verbose: methods = [ 'default', 'primal simplex', 'dual simplex', 'network simplex', 'barrier', 'sifting', 'concurrent' ] if len(H) == 0 or not any(any(H)): if verbose: stdout.write('CPLEX Version %s -- %s LP solver\n' % (vstr, methods[cplex_opt['lpmethod'] + 1])) x, f, eflag, output, lam = \ cplexlp(c, Ai, bi, Ae, be, xmin, xmax, x0, cplex_opt) else: if verbose: stdout.write('CPLEX Version %s -- %s QP solver\n' % (vstr, methods[cplex_opt['qpmethod'] + 1])) ## ensure H is numerically symmetric if H != H.T: H = (H + H.T) / 2 x, f, eflag, output, lam = \ cplexqp(H, c, Ai, bi, Ae, be, xmin, xmax, x0, cplex_opt) ## check for empty results (in case optimization failed) if len(x) == 0: x = NaN * zeros(nx) if len(f) == 0: f = NaN if len(lam) == 0: lam['ineqlin'] = NaN * zeros(len(bi)) lam['eqlin'] = NaN * zeros(len(be)) lam['lower'] = NaN * zeros(nx) lam['upper'] = NaN * zeros(nx) mu_l = NaN * zeros(nA) mu_u = NaN * zeros(nA) else: mu_l = zeros(nA) mu_u = zeros(nA) if unconstrained: lam['eqlin'] = array([]) ## negate prices depending on version if vnum < 12.3: lam['eqlin'] = -lam['eqlin'] lam['ineqlin'] = -lam['ineqlin'] ## repackage lambdas kl = find(lam.eqlin < 0) ## lower bound binding ku = find(lam.eqlin > 0) ## upper bound binding mu_l[ieq[kl]] = -lam['eqlin'][kl] mu_l[igt] = lam['ineqlin'][nlt + arange(ngt)] mu_l[ibx] = lam['ineqlin'][nlt + ngt + nbx + arange(nbx)] mu_u[ieq[ku]] = lam['eqlin'][ku] mu_u[ilt] = lam['ineqlin'][:nlt] mu_u[ibx] = lam['ineqlin'][nlt + ngt + arange(nbx)] lmbda = { 'mu_l': mu_l, 'mu_u': mu_u, 'lower': lam.lower, 'upper': lam.upper } return x, f, eflag, output, lmbda
def dcopf_solver(om, ppopt, out_opt=None): """Solves a DC optimal power flow. Inputs are an OPF model object, a PYPOWER options dict and a dict containing fields (can be empty) for each of the desired optional output fields. Outputs are a C{results} dict, C{success} flag and C{raw} output dict. C{results} is a PYPOWER case dict (ppc) with the usual baseMVA, bus branch, gen, gencost fields, along with the following additional fields: - C{order} see 'help ext2int' for details of this field - C{x} final value of optimization variables (internal order) - C{f} final objective function value - C{mu} shadow prices on ... - C{var} - C{l} lower bounds on variables - C{u} upper bounds on variables - C{lin} - C{l} lower bounds on linear constraints - C{u} upper bounds on linear constraints - C{g} (optional) constraint values - C{dg} (optional) constraint 1st derivatives - C{df} (optional) obj fun 1st derivatives (not yet implemented) - C{d2f} (optional) obj fun 2nd derivatives (not yet implemented) C{success} is C{True} if solver converged successfully, C{False} otherwise. C{raw} is a raw output dict in form returned by MINOS - C{xr} final value of optimization variables - C{pimul} constraint multipliers - C{info} solver specific termination code - C{output} solver specific output information @see: L{opf}, L{qps_pypower} @author: Ray Zimmerman (PSERC Cornell) @author: Carlos E. Murillo-Sanchez (PSERC Cornell & Universidad Autonoma de Manizales) """ if out_opt is None: out_opt = {} ## options verbose = ppopt['VERBOSE'] alg = ppopt['OPF_ALG_DC'] if alg == 0: if have_fcn('cplex'): ## use CPLEX by default, if available alg = 500 elif have_fcn('mosek'): ## if not, then MOSEK, if available alg = 600 elif have_fcn('gurobi'): ## if not, then Gurobi, if available alg = 700 else: ## otherwise PIPS alg = 200 ## unpack data ppc = om.get_ppc() baseMVA, bus, gen, branch, gencost = \ ppc["baseMVA"], ppc["bus"], ppc["gen"], ppc["branch"], ppc["gencost"] cp = om.get_cost_params() N, H, Cw = cp["N"], cp["H"], cp["Cw"] fparm = array(c_[cp["dd"], cp["rh"], cp["kk"], cp["mm"]]) Bf = om.userdata('Bf') Pfinj = om.userdata('Pfinj') vv, ll, _, _ = om.get_idx() ## problem dimensions ipol = find(gencost[:, MODEL] == POLYNOMIAL) ## polynomial costs ipwl = find(gencost[:, MODEL] == PW_LINEAR) ## piece-wise linear costs nb = bus.shape[0] ## number of buses nl = branch.shape[0] ## number of branches nw = N.shape[0] ## number of general cost vars, w ny = om.getN('var', 'y') ## number of piece-wise linear costs nxyz = om.getN('var') ## total number of control vars of all types ## linear constraints & variable bounds A, l, u = om.linear_constraints() x0, xmin, xmax = om.getv() ## set up objective function of the form: f = 1/2 * X'*HH*X + CC'*X ## where X = [x;y;z]. First set up as quadratic function of w, ## f = 1/2 * w'*HHw*w + CCw'*w, where w = diag(M) * (N*X - Rhat). We ## will be building on the (optionally present) user supplied parameters. ## piece-wise linear costs any_pwl = int(ny > 0) if any_pwl: # Sum of y vars. Npwl = sparse((ones(ny), (zeros(ny), arange(vv["i1"]["y"], vv["iN"]["y"]))), (1, nxyz)) Hpwl = sparse((1, 1)) Cpwl = array([1]) fparm_pwl = array([[1, 0, 0, 1]]) else: Npwl = None#zeros((0, nxyz)) Hpwl = None#array([]) Cpwl = array([]) fparm_pwl = zeros((0, 4)) ## quadratic costs npol = len(ipol) if any(find(gencost[ipol, NCOST] > 3)): stderr.write('DC opf cannot handle polynomial costs with higher ' 'than quadratic order.\n') iqdr = find(gencost[ipol, NCOST] == 3) ilin = find(gencost[ipol, NCOST] == 2) polycf = zeros((npol, 3)) ## quadratic coeffs for Pg if len(iqdr) > 0: polycf[iqdr, :] = gencost[ipol[iqdr], COST:COST + 3] if npol: polycf[ilin, 1:3] = gencost[ipol[ilin], COST:COST + 2] polycf = dot(polycf, diag([ baseMVA**2, baseMVA, 1])) ## convert to p.u. if npol: Npol = sparse((ones(npol), (arange(npol), vv["i1"]["Pg"] + ipol)), (npol, nxyz)) # Pg vars Hpol = sparse((2 * polycf[:, 0], (arange(npol), arange(npol))), (npol, npol)) else: Npol = None Hpol = None Cpol = polycf[:, 1] fparm_pol = ones((npol, 1)) * array([[1, 0, 0, 1]]) ## combine with user costs NN = vstack([n for n in [Npwl, Npol, N] if n is not None and n.shape[0] > 0], "csr") # FIXME: Zero dimension sparse matrices. if (Hpwl is not None) and any_pwl and (npol + nw): Hpwl = hstack([Hpwl, sparse((any_pwl, npol + nw))]) if Hpol is not None: if any_pwl and npol: Hpol = hstack([sparse((npol, any_pwl)), Hpol]) if npol and nw: Hpol = hstack([Hpol, sparse((npol, nw))]) if (H is not None) and nw and (any_pwl + npol): H = hstack([sparse((nw, any_pwl + npol)), H]) HHw = vstack([h for h in [Hpwl, Hpol, H] if h is not None and h.shape[0] > 0], "csr") CCw = r_[Cpwl, Cpol, Cw] ffparm = r_[fparm_pwl, fparm_pol, fparm] ## transform quadratic coefficients for w into coefficients for X nnw = any_pwl + npol + nw M = sparse((ffparm[:, 3], (range(nnw), range(nnw)))) MR = M * ffparm[:, 1] HMR = HHw * MR MN = M * NN HH = MN.T * HHw * MN CC = MN.T * (CCw - HMR) C0 = 0.5 * dot(MR, HMR) + sum(polycf[:, 2]) # Constant term of cost. ## set up input for QP solver opt = {'alg': alg, 'verbose': verbose} if (alg == 200) or (alg == 250): ## try to select an interior initial point Varefs = bus[bus[:, BUS_TYPE] == REF, VA] * (pi / 180.0) lb, ub = xmin.copy(), xmax.copy() lb[xmin == -Inf] = -1e10 ## replace Inf with numerical proxies ub[xmax == Inf] = 1e10 x0 = (lb + ub) / 2; # angles set to first reference angle x0[vv["i1"]["Va"]:vv["iN"]["Va"]] = Varefs[0] if ny > 0: ipwl = find(gencost[:, MODEL] == PW_LINEAR) # largest y-value in CCV data c = gencost.flatten('F')[sub2ind(gencost.shape, ipwl, NCOST + 2 * gencost[ipwl, NCOST])] x0[vv["i1"]["y"]:vv["iN"]["y"]] = max(c) + 0.1 * abs(max(c)) ## set up options feastol = ppopt['PDIPM_FEASTOL'] gradtol = ppopt['PDIPM_GRADTOL'] comptol = ppopt['PDIPM_COMPTOL'] costtol = ppopt['PDIPM_COSTTOL'] max_it = ppopt['PDIPM_MAX_IT'] max_red = ppopt['SCPDIPM_RED_IT'] if feastol == 0: feastol = ppopt['OPF_VIOLATION'] ## = OPF_VIOLATION by default opt["pips_opt"] = { 'feastol': feastol, 'gradtol': gradtol, 'comptol': comptol, 'costtol': costtol, 'max_it': max_it, 'max_red': max_red, 'cost_mult': 1 } elif alg == 400: opt['ipopt_opt'] = ipopt_options([], ppopt) elif alg == 500: opt['cplex_opt'] = cplex_options([], ppopt) elif alg == 600: opt['mosek_opt'] = mosek_options([], ppopt) elif alg == 700: opt['grb_opt'] = gurobi_options([], ppopt) else: raise ValueError("Unrecognised solver [%d]." % alg) ##----- run opf ----- x, f, info, output, lmbda = \ qps_pypower(HH, CC, A, l, u, xmin, xmax, x0, opt) success = (info == 1) ##----- calculate return values ----- if not any(isnan(x)): ## update solution data Va = x[vv["i1"]["Va"]:vv["iN"]["Va"]] Pg = x[vv["i1"]["Pg"]:vv["iN"]["Pg"]] f = f + C0 ## update voltages & generator outputs bus[:, VA] = Va * 180 / pi gen[:, PG] = Pg * baseMVA ## compute branch flows branch[:, [QF, QT]] = zeros((nl, 2)) branch[:, PF] = (Bf * Va + Pfinj) * baseMVA branch[:, PT] = -branch[:, PF] ## package up results mu_l = lmbda["mu_l"] mu_u = lmbda["mu_u"] muLB = lmbda["lower"] muUB = lmbda["upper"] ## update Lagrange multipliers il = find((branch[:, RATE_A] != 0) & (branch[:, RATE_A] < 1e10)) bus[:, [LAM_P, LAM_Q, MU_VMIN, MU_VMAX]] = zeros((nb, 4)) gen[:, [MU_PMIN, MU_PMAX, MU_QMIN, MU_QMAX]] = zeros((gen.shape[0], 4)) branch[:, [MU_SF, MU_ST]] = zeros((nl, 2)) bus[:, LAM_P] = (mu_u[ll["i1"]["Pmis"]:ll["iN"]["Pmis"]] - mu_l[ll["i1"]["Pmis"]:ll["iN"]["Pmis"]]) / baseMVA branch[il, MU_SF] = mu_u[ll["i1"]["Pf"]:ll["iN"]["Pf"]] / baseMVA branch[il, MU_ST] = mu_u[ll["i1"]["Pt"]:ll["iN"]["Pt"]] / baseMVA gen[:, MU_PMIN] = muLB[vv["i1"]["Pg"]:vv["iN"]["Pg"]] / baseMVA gen[:, MU_PMAX] = muUB[vv["i1"]["Pg"]:vv["iN"]["Pg"]] / baseMVA pimul = r_[ mu_l - mu_u, -ones(int(ny > 0)), ## dummy entry corresponding to linear cost row in A muLB - muUB ] mu = { 'var': {'l': muLB, 'u': muUB}, 'lin': {'l': mu_l, 'u': mu_u} } results = deepcopy(ppc) results["bus"], results["branch"], results["gen"], \ results["om"], results["x"], results["mu"], results["f"] = \ bus, branch, gen, om, x, mu, f raw = {'xr': x, 'pimul': pimul, 'info': info, 'output': output} return results, success, raw
def t_qps_pypower(quiet=False): """Tests of C{qps_pypower} QP solvers. @author: Ray Zimmerman (PSERC Cornell) @author: Richard Lincoln """ algs = [200, 250, 400, 500, 600, 700] names = ['PIPS', 'sc-PIPS', 'IPOPT', 'CPLEX', 'MOSEK', 'Gurobi'] check = [None, None, 'ipopt', 'cplex', 'mosek', 'gurobipy'] n = 36 t_begin(n * len(algs), quiet) for k in range(len(algs)): if check[k] is not None and not have_fcn(check[k]): t_skip(n, '%s not installed' % names[k]) else: opt = {'verbose': 0, 'alg': algs[k]} if names[k] == 'PIPS' or names[k] == 'sc-PIPS': opt['pips_opt'] = {} opt['pips_opt']['comptol'] = 1e-8 if names[k] == 'CPLEX': # alg = 0 ## default uses barrier method with NaN bug in lower lim multipliers alg = 2 ## use dual simplex ppopt = ppoption(CPLEX_LPMETHOD=alg, CPLEX_QPMETHOD=min([4, alg])) opt['cplex_opt'] = cplex_options([], ppopt) if names[k] == 'MOSEK': # alg = 5 ## use dual simplex ppopt = ppoption() # ppopt = ppoption(ppopt, MOSEK_LP_ALG = alg) ppopt = ppoption(ppopt, MOSEK_GAP_TOL=1e-9) opt['mosek_opt'] = mosek_options([], ppopt) t = '%s - 3-d LP : ' % names[k] ## example from 'doc linprog' c = array([-5, -4, -6], float) A = sparse([[1, -1, 1], [3, 2, 4], [3, 2, 0]], dtype=float) l = None u = array([20, 42, 30], float) xmin = array([0, 0, 0], float) x0 = None x, f, s, _, lam = qps_pypower(None, c, A, l, u, xmin, None, None, opt) t_is(s, 1, 12, [t, 'success']) t_is(x, [0, 15, 3], 6, [t, 'x']) t_is(f, -78, 6, [t, 'f']) t_is(lam['mu_l'], [0, 0, 0], 13, [t, 'lam.mu_l']) t_is(lam['mu_u'], [0, 1.5, 0.5], 9, [t, 'lam.mu_u']) t_is(lam['lower'], [1, 0, 0], 9, [t, 'lam.lower']) t_is(lam['upper'], zeros(shape(x)), 13, [t, 'lam.upper']) t = '%s - unconstrained 3-d quadratic : ' % names[k] ## from http://www.akiti.ca/QuadProgEx0Constr.html H = sparse([[5, -2, -1], [-2, 4, 3], [-1, 3, 5]], dtype=float) c = array([2, -35, -47], float) x0 = array([0, 0, 0], float) x, f, s, _, lam = qps_pypower(H, c, opt=opt) t_is(s, 1, 12, [t, 'success']) t_is(x, [3, 5, 7], 8, [t, 'x']) t_is(f, -249, 13, [t, 'f']) t_ok(len(lam['mu_l']) == 0, [t, 'lam.mu_l']) t_ok(len(lam['mu_u']) == 0, [t, 'lam.mu_u']) t_is(lam['lower'], zeros(shape(x)), 13, [t, 'lam.lower']) t_is(lam['upper'], zeros(shape(x)), 13, [t, 'lam.upper']) t = '%s - constrained 2-d QP : ' % names[k] ## example from 'doc quadprog' H = sparse([[1, -1], [-1, 2]], dtype=float) c = array([-2, -6], float) A = sparse([[1, 1], [-1, 2], [2, 1]], dtype=float) l = None u = array([2, 2, 3], float) xmin = array([0, 0]) x0 = None x, f, s, _, lam = qps_pypower(H, c, A, l, u, xmin, None, x0, opt) t_is(s, 1, 12, [t, 'success']) t_is(x, array([2., 4.]) / 3, 7, [t, 'x']) t_is(f, -74. / 9, 6, [t, 'f']) t_is(lam['mu_l'], [0., 0., 0.], 13, [t, 'lam.mu_l']) t_is(lam['mu_u'], array([28., 4., 0.]) / 9, 7, [t, 'lam.mu_u']) t_is(lam['lower'], zeros(shape(x)), 8, [t, 'lam.lower']) t_is(lam['upper'], zeros(shape(x)), 13, [t, 'lam.upper']) t = '%s - constrained 4-d QP : ' % names[k] ## from http://www.jmu.edu/docs/sasdoc/sashtml/iml/chap8/sect12.htm H = sparse([[1003.1, 4.3, 6.3, 5.9], [4.3, 2.2, 2.1, 3.9], [6.3, 2.1, 3.5, 4.8], [5.9, 3.9, 4.8, 10.0]]) c = zeros(4) A = sparse([[1, 1, 1, 1], [0.17, 0.11, 0.10, 0.18]]) l = array([1, 0.10]) u = array([1, Inf]) xmin = zeros(4) x0 = array([1, 0, 0, 1], float) x, f, s, _, lam = qps_pypower(H, c, A, l, u, xmin, None, x0, opt) t_is(s, 1, 12, [t, 'success']) t_is(x, array([0, 2.8, 0.2, 0]) / 3, 5, [t, 'x']) t_is(f, 3.29 / 3, 6, [t, 'f']) t_is(lam['mu_l'], array([6.58, 0]) / 3, 6, [t, 'lam.mu_l']) t_is(lam['mu_u'], [0, 0], 13, [t, 'lam.mu_u']) t_is(lam['lower'], [2.24, 0, 0, 1.7667], 4, [t, 'lam.lower']) t_is(lam['upper'], zeros(shape(x)), 13, [t, 'lam.upper']) t = '%s - (dict) constrained 4-d QP : ' % names[k] p = { 'H': H, 'A': A, 'l': l, 'u': u, 'xmin': xmin, 'x0': x0, 'opt': opt } x, f, s, _, lam = qps_pypower(p) t_is(s, 1, 12, [t, 'success']) t_is(x, array([0, 2.8, 0.2, 0]) / 3, 5, [t, 'x']) t_is(f, 3.29 / 3, 6, [t, 'f']) t_is(lam['mu_l'], array([6.58, 0]) / 3, 6, [t, 'lam.mu_l']) t_is(lam['mu_u'], [0, 0], 13, [t, 'lam.mu_u']) t_is(lam['lower'], [2.24, 0, 0, 1.7667], 4, [t, 'lam.lower']) t_is(lam['upper'], zeros(shape(x)), 13, [t, 'lam.upper']) t = '%s - infeasible LP : ' % names[k] p = { 'A': sparse([1, 1]), 'c': array([1, 1]), 'u': array([-1]), 'xmin': array([0, 0]), 'opt': opt } x, f, s, _, lam = qps_pypower(p) t_ok(s <= 0, [t, 'no success']) t_end()
def qps_cplex(H, c, A, l, u, xmin, xmax, x0, opt): """Quadratic Program Solver based on CPLEX. A wrapper function providing a PYPOWER standardized interface for using C{cplexqp} or C{cplexlp} to solve the following QP (quadratic programming) problem:: min 1/2 X'*H*x + c'*x x subject to:: l <= A*x <= u (linear constraints) xmin <= x <= xmax (variable bounds) Inputs (all optional except C{H}, C{c}, C{A} and C{l}): - C{H} : matrix (possibly sparse) of quadratic cost coefficients - C{c} : vector of linear cost coefficients - C{A, l, u} : define the optional linear constraints. Default values for the elements of L and U are -Inf and Inf, respectively. - C{xmin, xmax} : optional lower and upper bounds on the C{x} variables, defaults are -Inf and Inf, respectively. - C{x0} : optional starting value of optimization vector C{x} - C{opt} : optional options structure with the following fields, all of which are also optional (default values shown in parentheses) - C{verbose} (0) - controls level of progress output displayed - 0 = no progress output - 1 = some progress output - 2 = verbose progress output - C{cplex_opt} - options dict for CPLEX, value in verbose overrides these options - C{problem} : The inputs can alternatively be supplied in a single C{problem} dict with fields corresponding to the input arguments described above: C{H, c, A, l, u, xmin, xmax, x0, opt} Outputs: - C{x} : solution vector - C{f} : final objective function value - C{exitflag} : CPLEXQP/CPLEXLP exit flag (see C{cplexqp} and C{cplexlp} documentation for details) - C{output} : CPLEXQP/CPLEXLP output dict (see C{cplexqp} and C{cplexlp} documentation for details) - C{lmbda} : dict containing the Langrange and Kuhn-Tucker multipliers on the constraints, with fields: - mu_l - lower (left-hand) limit on linear constraints - mu_u - upper (right-hand) limit on linear constraints - lower - lower bound on optimization variables - upper - upper bound on optimization variables @author: Ray Zimmerman (PSERC Cornell) @author: Richard Lincoln """ ##----- input argument handling ----- ## gather inputs if isinstance(H, dict): ## problem struct p = H if 'opt' in p: opt = p['opt'] if 'x0' in p: x0 = p['x0'] if 'xmax' in p: xmax = p['xmax'] if 'xmin' in p: xmin = p['xmin'] if 'u' in p: u = p['u'] if 'l' in p: l = p['l'] if 'A' in p: A = p['A'] if 'c' in p: c = p['c'] if 'H' in p: H = p['H'] else: ## individual args assert H is not None assert c is not None assert A is not None assert l is not None if opt is None: opt = {} # if x0 is None: # x0 = array([]) # if xmax is None: # xmax = array([]) # if xmin is None: # xmin = array([]) ## define nx, set default values for missing optional inputs if len(H) == 0 or not any(any(H)): if len(A) == 0 and len(xmin) == 0 and len(xmax) == 0: stderr.write('qps_cplex: LP problem must include constraints or variable bounds\n') else: if len(A) > 0: nx = shape(A)[1] elif len(xmin) > 0: nx = len(xmin) else: # if len(xmax) > 0 nx = len(xmax) else: nx = shape(H)[0] if len(c) == 0: c = zeros(nx) if len(A) > 0 and (len(l) == 0 or all(l == -Inf)) and \ (len(u) == 0 or all(u == Inf)): A = None ## no limits => no linear constraints nA = shape(A)[0] ## number of original linear constraints if len(u) == 0: ## By default, linear inequalities are ... u = Inf * ones(nA) ## ... unbounded above and ... if len(l) == 0: l = -Inf * ones(nA) ## ... unbounded below. if len(xmin) == 0: ## By default, optimization variables are ... xmin = -Inf * ones(nx) ## ... unbounded below and ... if len(xmax) == 0: xmax = Inf * ones(nx) ## ... unbounded above. if len(x0) == 0: x0 = zeros(nx) ## default options if 'verbose' in opt: verbose = opt['verbose'] else: verbose = 0 #if 'max_it' in opt: # max_it = opt['max_it'] #else: # max_it = 0 ## split up linear constraints ieq = find( abs(u-l) <= EPS ) ## equality igt = find( u >= 1e10 & l > -1e10 ) ## greater than, unbounded above ilt = find( l <= -1e10 & u < 1e10 ) ## less than, unbounded below ibx = find( (abs(u-l) > EPS) & (u < 1e10) & (l > -1e10) ) Ae = A[ieq, :] be = u[ieq] Ai = r_[ A[ilt, :], -A[igt, :], A[ibx, :] -A[ibx, :] ] bi = r_[ u[ilt], -l[igt], u[ibx], -l[ibx] ] ## grab some dimensions nlt = len(ilt) ## number of upper bounded linear inequalities ngt = len(igt) ## number of lower bounded linear inequalities nbx = len(ibx) ## number of doubly bounded linear inequalities ## set up options struct for CPLEX if 'cplex_opt' in opt: cplex_opt = cplex_options(opt['cplex_opt']) else: cplex_opt = cplex_options cplex = Cplex('null') vstr = cplex.getVersion s, e, tE, m, t = re.compile(vstr, '(\d+\.\d+)\.') vnum = int(t[0][0]) vrb = max([0, verbose - 1]) cplex_opt['barrier']['display'] = vrb cplex_opt['conflict']['display'] = vrb cplex_opt['mip']['display'] = vrb cplex_opt['sifting']['display'] = vrb cplex_opt['simplex']['display'] = vrb cplex_opt['tune']['display'] = vrb if vrb and (vnum > 12.2): cplex_opt['diagnostics'] = 'on' #if max_it: # cplex_opt. ## not sure what to set here if len(Ai) == 0 and len(Ae) == 0: unconstrained = 1 Ae = sparse((1, nx)) be = 0 else: unconstrained = 0 ## call the solver if verbose: methods = [ 'default', 'primal simplex', 'dual simplex', 'network simplex', 'barrier', 'sifting', 'concurrent' ] if len(H) == 0 or not any(any(H)): if verbose: stdout.write('CPLEX Version %s -- %s LP solver\n' % (vstr, methods[cplex_opt['lpmethod'] + 1])) x, f, eflag, output, lam = \ cplexlp(c, Ai, bi, Ae, be, xmin, xmax, x0, cplex_opt) else: if verbose: stdout.write('CPLEX Version %s -- %s QP solver\n' % (vstr, methods[cplex_opt['qpmethod'] + 1])) ## ensure H is numerically symmetric if H != H.T: H = (H + H.T) / 2 x, f, eflag, output, lam = \ cplexqp(H, c, Ai, bi, Ae, be, xmin, xmax, x0, cplex_opt) ## check for empty results (in case optimization failed) if len(x) == 0: x = NaN * zeros(nx) if len(f) == 0: f = NaN if len(lam) == 0: lam['ineqlin'] = NaN * zeros(len(bi)) lam['eqlin'] = NaN * zeros(len(be)) lam['lower'] = NaN * zeros(nx) lam['upper'] = NaN * zeros(nx) mu_l = NaN * zeros(nA) mu_u = NaN * zeros(nA) else: mu_l = zeros(nA) mu_u = zeros(nA) if unconstrained: lam['eqlin'] = array([]) ## negate prices depending on version if vnum < 12.3: lam['eqlin'] = -lam['eqlin'] lam['ineqlin'] = -lam['ineqlin'] ## repackage lambdas kl = find(lam.eqlin < 0) ## lower bound binding ku = find(lam.eqlin > 0) ## upper bound binding mu_l[ieq[kl]] = -lam['eqlin'][kl] mu_l[igt] = lam['ineqlin'][nlt + arange(ngt)] mu_l[ibx] = lam['ineqlin'][nlt + ngt + nbx + arange(nbx)] mu_u[ieq[ku]] = lam['eqlin'][ku] mu_u[ilt] = lam['ineqlin'][:nlt] mu_u[ibx] = lam['ineqlin'][nlt + ngt + arange(nbx)] lmbda = { 'mu_l': mu_l, 'mu_u': mu_u, 'lower': lam.lower, 'upper': lam.upper } return x, f, eflag, output, lmbda