예제 #1
0
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
예제 #2
0
파일: dcopf_solver.py 프로젝트: rwl/PYPOWER
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
예제 #3
0
def qps_gurobi(H, c, A, l, u, xmin, xmax, x0, opt):
    """Quadratic Program Solver based on GUROBI.

    A wrapper function providing a PYPOWER standardized interface for using
    gurobipy 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 H, c, A and l):
        H : matrix (possibly sparse) of quadratic cost coefficients
        c : vector of linear cost coefficients
        A, l, u : define the optional linear constraints. Default
            values for the elements of l and u are -Inf and Inf,
            respectively.
        xmin, xmax : optional lower and upper bounds on the
            C{x} variables, defaults are -Inf and Inf, respectively.
        x0 : optional starting value of optimization vector C{x}
        opt : optional options structure with the following fields,
            all of which are also optional (default values shown in
            parentheses)
            verbose (0) - controls level of progress output displayed
                0 = no progress output
                1 = some progress output
                2 = verbose progress output
            grb_opt - options dict for Gurobi, value in
                verbose overrides these options
        problem : The inputs can alternatively be supplied in a single
            PROBLEM dict with fields corresponding to the input arguments
            described above: H, c, A, l, u, xmin, xmax, x0, opt

    Outputs:
        x : solution vector
        f : final objective function value
        exitflag : gurobipy exit flag
            1 = converged
            0 or negative values = negative of GUROBI_MEX exit flag
            (see gurobipy documentation for details)
        output : gurobipy output dict
            (see gurobipy documentation for details)
        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

    Note the calling syntax is almost identical to that of QUADPROG
    from MathWorks' Optimization Toolbox. The main difference is that
    the linear constraints are specified with A, l, u instead of
    A, b, Aeq, beq.

    Calling syntax options:
        x, f, exitflag, output, lmbda = ...
            qps_gurobi(H, c, A, l, u, xmin, xmax, x0, opt)

        r = qps_gurobi(H, c, A, l, u)
        r = qps_gurobi(H, c, A, l, u, xmin, xmax)
        r = qps_gurobi(H, c, A, l, u, xmin, xmax, x0)
        r = qps_gurobi(H, c, A, l, u, xmin, xmax, x0, opt)
        r = qps_gurobi(problem), where problem is a dict with fields:
                        H, c, A, l, u, xmin, xmax, x0, opt
                        all fields except 'c', 'A' and 'l' or 'u' are optional

    Example: (problem from from http://www.jmu.edu/docs/sasdoc/sashtml/iml/chap8/sect12.htm)
        H = [   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  ]
        c = zeros((4, 1))
        A = [   [1       1       1       1]
                [0.17    0.11    0.10    0.18]    ]
        l = [1; 0.10]
        u = [1; Inf]
        xmin = zeros((4, 1))
        x0 = [1; 0; 0; 1]
        opt = {'verbose': 2}
        x, f, s, out, lmbda = qps_gurobi(H, c, A, l, u, xmin, [], x0, opt)

    @see: L{gurobipy}.
    """
    import gurobipy

    ##----- 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_gurobi: 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)
        H = sparse((nx, nx))
    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 nA:
        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(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

    ## set up options struct for Gurobi
    if 'grb_opt' in opt:
        g_opt = gurobi_options(opt['grb_opt'])
    else:
        g_opt = gurobi_options()

    g_opt['Display'] = min(verbose, 3)
    if verbose:
        g_opt['DisplayInterval'] = 1
    else:
        g_opt['DisplayInterval'] = Inf

    if not issparse(A):
        A = sparse(A)

    ## 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) )

    ## 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
    neq = len(ieq)      ## number of equalities
    niq = nlt + ngt + 2 * nbx    ## number of inequalities

    AA  = [ A[ieq, :], A[ilt, :], -A[igt, :], A[ibx, :], -A[ibx, :] ]
    bb  = [ u[ieq],    u[ilt],    -l[igt],    u[ibx],    -l[ibx]    ]
    contypes = '=' * neq + '<' * niq

    ## call the solver
    if len(H) == 0 or not any(any(H)):
        lpqp = 'LP'
    else:
        lpqp = 'QP'
        rr, cc, vv = find(H)
        g_opt['QP']['qrow'] = int(rr.T - 1)
        g_opt['QP']['qcol'] = int(cc.T - 1)
        g_opt['QP']['qval'] = 0.5 * vv.T

    if verbose:
        methods = [
            'primal simplex',
            'dual simplex',
            'interior point',
            'concurrent',
            'deterministic concurrent'
        ]
        print('Gurobi Version %s -- %s %s solver\n'
              '<unknown>' % (methods[g_opt['Method'] + 1], lpqp))

    x, f, eflag, output, lmbda = \
        gurobipy(c.T, 1, AA, bb, contypes, xmin, xmax, 'C', g_opt)
    pi  = lmbda['Pi']
    rc  = lmbda['RC']
    output['flag'] = eflag
    if eflag == 2:
        eflag = 1          ## optimal solution found
    else:
        eflag = -eflag     ## failed somehow

    ## check for empty results (in case optimization failed)
    lam = {}
    if len(x) == 0:
        x = NaN(nx, 1);
        lam['lower']   = NaN(nx)
        lam['upper']   = NaN(nx)
    else:
        lam['lower']   = zeros(nx)
        lam['upper']   = zeros(nx)

    if len(f) == 0:
        f = NaN

    if len(pi) == 0:
        pi  = NaN(len(bb))

    kl = find(rc > 0);   ## lower bound binding
    ku = find(rc < 0);   ## upper bound binding
    lam['lower'][kl]   =  rc[kl]
    lam['upper'][ku]   = -rc[ku]
    lam['eqlin']   = pi[:neq + 1]
    lam['ineqlin'] = pi[neq + range(niq + 1)]
    mu_l        = zeros(nA)
    mu_u        = zeros(nA)

    ## repackage lmbdas
    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 + range(ngt + 1)]
    mu_l[ibx] = -lam['ineqlin'][nlt + ngt + nbx + range(nbx)]

    mu_u[ieq[ku]] = -lam['eqlin'][ku]
    mu_u[ilt] = -lam['ineqlin'][:nlt + 1]
    mu_u[ibx] = -lam['ineqlin'][nlt + ngt + range(nbx + 1)]

    lmbda = {
        'mu_l': mu_l,
        'mu_u': mu_u,
        'lower': lam['lower'],
        'upper': lam['upper']
    }

    return x, f, eflag, output, lmbda
예제 #4
0
def qps_gurobi(H, c, A, l, u, xmin, xmax, x0, opt):
    """Quadratic Program Solver based on GUROBI.

    A wrapper function providing a PYPOWER standardized interface for using
    gurobipy 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 H, c, A and l):
        H : matrix (possibly sparse) of quadratic cost coefficients
        c : vector of linear cost coefficients
        A, l, u : define the optional linear constraints. Default
            values for the elements of l and u are -Inf and Inf,
            respectively.
        xmin, xmax : optional lower and upper bounds on the
            C{x} variables, defaults are -Inf and Inf, respectively.
        x0 : optional starting value of optimization vector C{x}
        opt : optional options structure with the following fields,
            all of which are also optional (default values shown in
            parentheses)
            verbose (0) - controls level of progress output displayed
                0 = no progress output
                1 = some progress output
                2 = verbose progress output
            grb_opt - options dict for Gurobi, value in
                verbose overrides these options
        problem : The inputs can alternatively be supplied in a single
            PROBLEM dict with fields corresponding to the input arguments
            described above: H, c, A, l, u, xmin, xmax, x0, opt

    Outputs:
        x : solution vector
        f : final objective function value
        exitflag : gurobipy exit flag
            1 = converged
            0 or negative values = negative of GUROBI_MEX exit flag
            (see gurobipy documentation for details)
        output : gurobipy output dict
            (see gurobipy documentation for details)
        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

    Note the calling syntax is almost identical to that of QUADPROG
    from MathWorks' Optimization Toolbox. The main difference is that
    the linear constraints are specified with A, l, u instead of
    A, b, Aeq, beq.

    Calling syntax options:
        x, f, exitflag, output, lmbda = ...
            qps_gurobi(H, c, A, l, u, xmin, xmax, x0, opt)

        r = qps_gurobi(H, c, A, l, u)
        r = qps_gurobi(H, c, A, l, u, xmin, xmax)
        r = qps_gurobi(H, c, A, l, u, xmin, xmax, x0)
        r = qps_gurobi(H, c, A, l, u, xmin, xmax, x0, opt)
        r = qps_gurobi(problem), where problem is a dict with fields:
                        H, c, A, l, u, xmin, xmax, x0, opt
                        all fields except 'c', 'A' and 'l' or 'u' are optional

    Example: (problem from from http://www.jmu.edu/docs/sasdoc/sashtml/iml/chap8/sect12.htm)
        H = [   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  ]
        c = zeros((4, 1))
        A = [   [1       1       1       1]
                [0.17    0.11    0.10    0.18]    ]
        l = [1; 0.10]
        u = [1; Inf]
        xmin = zeros((4, 1))
        x0 = [1; 0; 0; 1]
        opt = {'verbose': 2}
        x, f, s, out, lmbda = qps_gurobi(H, c, A, l, u, xmin, [], x0, opt)

    @see: L{gurobipy}.
    """
    import gurobipy

    ##----- 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_gurobi: 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)
        H = sparse((nx, nx))
    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 nA:
        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(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

    ## set up options struct for Gurobi
    if 'grb_opt' in opt:
        g_opt = gurobi_options(opt['grb_opt'])
    else:
        g_opt = gurobi_options()

    g_opt['Display'] = min(verbose, 3)
    if verbose:
        g_opt['DisplayInterval'] = 1
    else:
        g_opt['DisplayInterval'] = Inf

    if not issparse(A):
        A = sparse(A)

    ## 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) )

    ## 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
    neq = len(ieq)      ## number of equalities
    niq = nlt + ngt + 2 * nbx    ## number of inequalities

    AA  = [ A[ieq, :], A[ilt, :], -A[igt, :], A[ibx, :], -A[ibx, :] ]
    bb  = [ u[ieq],    u[ilt],    -l[igt],    u[ibx],    -l[ibx]    ]
    contypes = '=' * neq + '<' * niq

    ## call the solver
    if len(H) == 0 or not any(any(H)):
        lpqp = 'LP'
    else:
        lpqp = 'QP'
        rr, cc, vv = find(H)
        g_opt['QP']['qrow'] = int(rr.T - 1)
        g_opt['QP']['qcol'] = int(cc.T - 1)
        g_opt['QP']['qval'] = 0.5 * vv.T

    if verbose:
        methods = [
            'primal simplex',
            'dual simplex',
            'interior point',
            'concurrent',
            'deterministic concurrent'
        ]
        print(('Gurobi Version %s -- %s %s solver\n'
              '<unknown>' % (methods[g_opt['Method'] + 1], lpqp)))

    x, f, eflag, output, lmbda = \
        gurobipy(c.T, 1, AA, bb, contypes, xmin, xmax, 'C', g_opt)
    pi  = lmbda['Pi']
    rc  = lmbda['RC']
    output['flag'] = eflag
    if eflag == 2:
        eflag = 1          ## optimal solution found
    else:
        eflag = -eflag     ## failed somehow

    ## check for empty results (in case optimization failed)
    lam = {}
    if len(x) == 0:
        x = NaN(nx, 1);
        lam['lower']   = NaN(nx)
        lam['upper']   = NaN(nx)
    else:
        lam['lower']   = zeros(nx)
        lam['upper']   = zeros(nx)

    if len(f) == 0:
        f = NaN

    if len(pi) == 0:
        pi  = NaN(len(bb))

    kl = find(rc > 0);   ## lower bound binding
    ku = find(rc < 0);   ## upper bound binding
    lam['lower'][kl]   =  rc[kl]
    lam['upper'][ku]   = -rc[ku]
    lam['eqlin']   = pi[:neq + 1]
    lam['ineqlin'] = pi[neq + list(range(niq + 1))]
    mu_l        = zeros(nA)
    mu_u        = zeros(nA)

    ## repackage lmbdas
    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 + list(range(ngt + 1))]
    mu_l[ibx] = -lam['ineqlin'][nlt + ngt + nbx + list(range(nbx))]

    mu_u[ieq[ku]] = -lam['eqlin'][ku]
    mu_u[ilt] = -lam['ineqlin'][:nlt + 1]
    mu_u[ibx] = -lam['ineqlin'][nlt + ngt + list(range(nbx + 1))]

    lmbda = {
        'mu_l': mu_l,
        'mu_u': mu_u,
        'lower': lam['lower'],
        'upper': lam['upper']
    }

    return x, f, eflag, output, lmbda