ltbams 0.9.9__py3-none-any.whl → 1.0.2a1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ams/__init__.py +4 -11
- ams/_version.py +3 -3
- ams/cases/5bus/pjm5bus_demo.xlsx +0 -0
- ams/cases/5bus/pjm5bus_jumper.xlsx +0 -0
- ams/cases/5bus/pjm5bus_uced.json +1062 -0
- ams/cases/5bus/pjm5bus_uced.xlsx +0 -0
- ams/cases/5bus/pjm5bus_uced_esd1.xlsx +0 -0
- ams/cases/5bus/pjm5bus_uced_ev.xlsx +0 -0
- ams/cases/ieee123/ieee123.xlsx +0 -0
- ams/cases/ieee123/ieee123_regcv1.xlsx +0 -0
- ams/cases/ieee14/ieee14.json +1166 -0
- ams/cases/ieee14/ieee14.raw +92 -0
- ams/cases/ieee14/ieee14_conn.xlsx +0 -0
- ams/cases/ieee14/ieee14_uced.xlsx +0 -0
- ams/cases/ieee39/ieee39.xlsx +0 -0
- ams/cases/ieee39/ieee39_uced.xlsx +0 -0
- ams/cases/ieee39/ieee39_uced_esd1.xlsx +0 -0
- ams/cases/ieee39/ieee39_uced_pvd1.xlsx +0 -0
- ams/cases/ieee39/ieee39_uced_vis.xlsx +0 -0
- ams/cases/matpower/benchmark.json +1594 -0
- ams/cases/matpower/case118.m +787 -0
- ams/cases/matpower/case14.m +129 -0
- ams/cases/matpower/case300.m +1315 -0
- ams/cases/matpower/case39.m +205 -0
- ams/cases/matpower/case5.m +62 -0
- ams/cases/matpower/case_ACTIVSg2000.m +9460 -0
- ams/cases/npcc/npcc.m +644 -0
- ams/cases/npcc/npcc_uced.xlsx +0 -0
- ams/cases/pglib/pglib_opf_case39_epri__api.m +243 -0
- ams/cases/wecc/wecc.m +714 -0
- ams/cases/wecc/wecc_uced.xlsx +0 -0
- ams/cli.py +6 -0
- ams/core/__init__.py +2 -0
- ams/core/documenter.py +652 -0
- ams/core/matprocessor.py +782 -0
- ams/core/model.py +330 -0
- ams/core/param.py +322 -0
- ams/core/service.py +918 -0
- ams/core/symprocessor.py +224 -0
- ams/core/var.py +59 -0
- ams/extension/__init__.py +5 -0
- ams/extension/eva.py +401 -0
- ams/interface.py +1085 -0
- ams/io/__init__.py +133 -0
- ams/io/json.py +82 -0
- ams/io/matpower.py +406 -0
- ams/io/psse.py +6 -0
- ams/io/pypower.py +103 -0
- ams/io/xlsx.py +80 -0
- ams/main.py +81 -4
- ams/models/__init__.py +24 -0
- ams/models/area.py +40 -0
- ams/models/bus.py +52 -0
- ams/models/cost.py +169 -0
- ams/models/distributed/__init__.py +3 -0
- ams/models/distributed/esd1.py +71 -0
- ams/models/distributed/ev.py +60 -0
- ams/models/distributed/pvd1.py +67 -0
- ams/models/group.py +231 -0
- ams/models/info.py +26 -0
- ams/models/line.py +238 -0
- ams/models/renewable/__init__.py +5 -0
- ams/models/renewable/regc.py +119 -0
- ams/models/reserve.py +94 -0
- ams/models/shunt.py +14 -0
- ams/models/static/__init__.py +2 -0
- ams/models/static/gen.py +165 -0
- ams/models/static/pq.py +61 -0
- ams/models/timeslot.py +69 -0
- ams/models/zone.py +49 -0
- ams/opt/__init__.py +12 -0
- ams/opt/constraint.py +175 -0
- ams/opt/exprcalc.py +127 -0
- ams/opt/expression.py +188 -0
- ams/opt/objective.py +174 -0
- ams/opt/omodel.py +432 -0
- ams/opt/optzbase.py +192 -0
- ams/opt/param.py +156 -0
- ams/opt/var.py +233 -0
- ams/pypower/__init__.py +8 -0
- ams/pypower/_compat.py +9 -0
- ams/pypower/core/__init__.py +8 -0
- ams/pypower/core/pips.py +894 -0
- ams/pypower/core/ppoption.py +244 -0
- ams/pypower/core/ppver.py +18 -0
- ams/pypower/core/solver.py +2451 -0
- ams/pypower/eps.py +6 -0
- ams/pypower/idx.py +174 -0
- ams/pypower/io.py +604 -0
- ams/pypower/make/__init__.py +11 -0
- ams/pypower/make/matrices.py +665 -0
- ams/pypower/make/pdv.py +506 -0
- ams/pypower/routines/__init__.py +7 -0
- ams/pypower/routines/cpf.py +513 -0
- ams/pypower/routines/cpf_callbacks.py +114 -0
- ams/pypower/routines/opf.py +1803 -0
- ams/pypower/routines/opffcns.py +1946 -0
- ams/pypower/routines/pflow.py +852 -0
- ams/pypower/toggle.py +1098 -0
- ams/pypower/utils.py +293 -0
- ams/report.py +212 -50
- ams/routines/__init__.py +23 -0
- ams/routines/acopf.py +117 -0
- ams/routines/cpf.py +65 -0
- ams/routines/dcopf.py +241 -0
- ams/routines/dcpf.py +209 -0
- ams/routines/dcpf0.py +196 -0
- ams/routines/dopf.py +150 -0
- ams/routines/ed.py +312 -0
- ams/routines/pflow.py +255 -0
- ams/routines/pflow0.py +113 -0
- ams/routines/routine.py +1033 -0
- ams/routines/rted.py +519 -0
- ams/routines/type.py +160 -0
- ams/routines/uc.py +376 -0
- ams/shared.py +63 -9
- ams/system.py +61 -22
- ams/utils/__init__.py +3 -0
- ams/utils/misc.py +77 -0
- ams/utils/paths.py +257 -0
- docs/Makefile +21 -0
- docs/make.bat +35 -0
- docs/source/_templates/autosummary/base.rst +5 -0
- docs/source/_templates/autosummary/class.rst +35 -0
- docs/source/_templates/autosummary/module.rst +65 -0
- docs/source/_templates/autosummary/module_toctree.rst +66 -0
- docs/source/api.rst +102 -0
- docs/source/conf.py +203 -0
- docs/source/examples/index.rst +34 -0
- docs/source/genmodelref.py +61 -0
- docs/source/genroutineref.py +47 -0
- docs/source/getting_started/copyright.rst +20 -0
- docs/source/getting_started/formats/index.rst +20 -0
- docs/source/getting_started/formats/matpower.rst +183 -0
- docs/source/getting_started/formats/psse.rst +46 -0
- docs/source/getting_started/formats/pypower.rst +223 -0
- docs/source/getting_started/formats/xlsx.png +0 -0
- docs/source/getting_started/formats/xlsx.rst +23 -0
- docs/source/getting_started/index.rst +76 -0
- docs/source/getting_started/install.rst +234 -0
- docs/source/getting_started/overview.rst +26 -0
- docs/source/getting_started/testcase.rst +45 -0
- docs/source/getting_started/verification.rst +13 -0
- docs/source/images/curent.ico +0 -0
- docs/source/images/dcopf_time.png +0 -0
- docs/source/images/sponsors/CURENT_Logo_NameOnTrans.png +0 -0
- docs/source/images/sponsors/CURENT_Logo_Transparent.png +0 -0
- docs/source/images/sponsors/CURENT_Logo_Transparent_Name.png +0 -0
- docs/source/images/sponsors/doe.png +0 -0
- docs/source/index.rst +108 -0
- docs/source/modeling/example.rst +159 -0
- docs/source/modeling/index.rst +17 -0
- docs/source/modeling/model.rst +210 -0
- docs/source/modeling/routine.rst +122 -0
- docs/source/modeling/system.rst +51 -0
- docs/source/release-notes.rst +398 -0
- ltbams-1.0.2a1.dist-info/METADATA +210 -0
- ltbams-1.0.2a1.dist-info/RECORD +188 -0
- {ltbams-0.9.9.dist-info → ltbams-1.0.2a1.dist-info}/WHEEL +1 -1
- ltbams-1.0.2a1.dist-info/top_level.txt +3 -0
- tests/__init__.py +0 -0
- tests/test_1st_system.py +33 -0
- tests/test_addressing.py +40 -0
- tests/test_andes_mats.py +61 -0
- tests/test_case.py +266 -0
- tests/test_cli.py +34 -0
- tests/test_export_csv.py +89 -0
- tests/test_group.py +83 -0
- tests/test_interface.py +216 -0
- tests/test_io.py +32 -0
- tests/test_jumper.py +27 -0
- tests/test_known_good.py +267 -0
- tests/test_matp.py +437 -0
- tests/test_model.py +54 -0
- tests/test_omodel.py +119 -0
- tests/test_paths.py +22 -0
- tests/test_report.py +251 -0
- tests/test_repr.py +21 -0
- tests/test_routine.py +178 -0
- tests/test_rtn_dcopf.py +101 -0
- tests/test_rtn_dcpf.py +77 -0
- tests/test_rtn_ed.py +279 -0
- tests/test_rtn_pflow.py +219 -0
- tests/test_rtn_rted.py +273 -0
- tests/test_rtn_uc.py +248 -0
- tests/test_service.py +73 -0
- ltbams-0.9.9.dist-info/LICENSE +0 -692
- ltbams-0.9.9.dist-info/METADATA +0 -859
- ltbams-0.9.9.dist-info/RECORD +0 -14
- ltbams-0.9.9.dist-info/top_level.txt +0 -1
- {ltbams-0.9.9.dist-info → ltbams-1.0.2a1.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,2451 @@
|
|
1
|
+
"""
|
2
|
+
PYPOWER solver interface.
|
3
|
+
"""
|
4
|
+
import logging
|
5
|
+
from functools import wraps
|
6
|
+
import re
|
7
|
+
|
8
|
+
import numpy as np
|
9
|
+
from numpy import flatnonzero as find
|
10
|
+
|
11
|
+
import scipy.sparse as sp
|
12
|
+
from scipy.sparse import csr_matrix as c_sparse
|
13
|
+
|
14
|
+
from andes.shared import rad2deg, deg2rad
|
15
|
+
|
16
|
+
from ams.shared import inf, nan
|
17
|
+
|
18
|
+
from ams.pypower.core.pips import pips
|
19
|
+
from ams.pypower.make import makeYbus
|
20
|
+
import ams.pypower.utils as putils
|
21
|
+
from ams.pypower.utils import IDX
|
22
|
+
from ams.pypower.routines.opffcns import opf_costfcn, opf_consfcn, opf_hessfcn
|
23
|
+
|
24
|
+
|
25
|
+
logger = logging.getLogger(__name__)
|
26
|
+
|
27
|
+
|
28
|
+
def require_mosek(f):
|
29
|
+
"""
|
30
|
+
Decorator for functions that require mosek.
|
31
|
+
"""
|
32
|
+
|
33
|
+
@wraps(f)
|
34
|
+
def wrapper(*args, **kwds):
|
35
|
+
try:
|
36
|
+
from pymosek import mosekopt # NOQA
|
37
|
+
except ImportError:
|
38
|
+
raise ModuleNotFoundError("Package `pymosek` needs to be manually installed.")
|
39
|
+
|
40
|
+
return f(*args, **kwds)
|
41
|
+
|
42
|
+
return wrapper
|
43
|
+
|
44
|
+
|
45
|
+
def require_cplex(f):
|
46
|
+
"""
|
47
|
+
Decorator for functions that require cplex.
|
48
|
+
"""
|
49
|
+
|
50
|
+
@wraps(f)
|
51
|
+
def wrapper(*args, **kwds):
|
52
|
+
try:
|
53
|
+
from cplex import Cplex, cplexlp, cplexqp, cplexoptimset # NOQA
|
54
|
+
except ImportError:
|
55
|
+
raise ModuleNotFoundError("Package `cplex` needs to be manually installed.")
|
56
|
+
|
57
|
+
return f(*args, **kwds)
|
58
|
+
|
59
|
+
return wrapper
|
60
|
+
|
61
|
+
|
62
|
+
def require_gurobi(f):
|
63
|
+
"""
|
64
|
+
Decorator for functions that require gurobi.
|
65
|
+
"""
|
66
|
+
|
67
|
+
@wraps(f)
|
68
|
+
def wrapper(*args, **kwds):
|
69
|
+
try:
|
70
|
+
from gurobipy import Model, GRB # NOQA
|
71
|
+
except ImportError:
|
72
|
+
raise ModuleNotFoundError("Package `gurobipy` needs to be manually installed.")
|
73
|
+
|
74
|
+
return f(*args, **kwds)
|
75
|
+
|
76
|
+
return wrapper
|
77
|
+
|
78
|
+
|
79
|
+
def require_ipopt(f):
|
80
|
+
"""
|
81
|
+
Decorator for functions that require ipopt.
|
82
|
+
"""
|
83
|
+
|
84
|
+
@wraps(f)
|
85
|
+
def wrapper(*args, **kwds):
|
86
|
+
try:
|
87
|
+
import pyipopt # NOQA
|
88
|
+
except ImportError:
|
89
|
+
raise ModuleNotFoundError("Package `pyipopt` needs to be manually installed.")
|
90
|
+
|
91
|
+
return f(*args, **kwds)
|
92
|
+
|
93
|
+
return wrapper
|
94
|
+
|
95
|
+
|
96
|
+
@require_ipopt
|
97
|
+
def ipoptopf_solver(om, ppopt):
|
98
|
+
"""
|
99
|
+
Solves AC optimal power flow using IPOPT.
|
100
|
+
|
101
|
+
Inputs are an OPF model object and a PYPOWER options vector.
|
102
|
+
|
103
|
+
Outputs are a C{results} dict, C{success} flag and C{raw} output dict.
|
104
|
+
|
105
|
+
C{results} is a PYPOWER case dict (ppc) with the usual C{baseMVA}, C{bus}
|
106
|
+
C{branch}, C{gen}, C{gencost} fields, along with the following additional
|
107
|
+
fields:
|
108
|
+
- C{order} see 'help ext2int' for details of this field
|
109
|
+
- C{x} final value of optimization variables (internal order)
|
110
|
+
- C{f} final objective function value
|
111
|
+
- C{mu} shadow prices on ...
|
112
|
+
- C{var}
|
113
|
+
- C{l} lower bounds on variables
|
114
|
+
- C{u} upper bounds on variables
|
115
|
+
- C{nln}
|
116
|
+
- C{l} lower bounds on nonlinear constraints
|
117
|
+
- C{u} upper bounds on nonlinear constraints
|
118
|
+
- C{lin}
|
119
|
+
- C{l} lower bounds on linear constraints
|
120
|
+
- C{u} upper bounds on linear constraints
|
121
|
+
|
122
|
+
C{success} is C{True} if solver converged successfully, C{False} otherwise
|
123
|
+
|
124
|
+
C{raw} is a raw output dict in form returned by MINOS
|
125
|
+
- C{xr} final value of optimization variables
|
126
|
+
- C{pimul} constraint multipliers
|
127
|
+
- C{info} solver specific termination code
|
128
|
+
- C{output} solver specific output information
|
129
|
+
|
130
|
+
@see: L{opf}, L{pips}
|
131
|
+
|
132
|
+
@author: Ray Zimmerman (PSERC Cornell)
|
133
|
+
@author: Carlos E. Murillo-Sanchez (PSERC Cornell & Universidad
|
134
|
+
Autonoma de Manizales)
|
135
|
+
"""
|
136
|
+
# unpack data
|
137
|
+
ppc = om.get_ppc()
|
138
|
+
baseMVA, bus, gen, branch, gencost = \
|
139
|
+
ppc['baseMVA'], ppc['bus'], ppc['gen'], ppc['branch'], ppc['gencost']
|
140
|
+
vv, _, nn, _ = om.get_idx()
|
141
|
+
|
142
|
+
# problem dimensions
|
143
|
+
nb = np.shape(bus)[0] # number of buses
|
144
|
+
ng = np.shape(gen)[0] # number of gens
|
145
|
+
nl = np.shape(branch)[0] # number of branches
|
146
|
+
ny = om.getN('var', 'y') # number of piece-wise linear costs
|
147
|
+
|
148
|
+
# linear constraints
|
149
|
+
A, l, u = om.linear_constraints()
|
150
|
+
|
151
|
+
# bounds on optimization vars
|
152
|
+
_, xmin, xmax = om.getv()
|
153
|
+
|
154
|
+
# build admittance matrices
|
155
|
+
Ybus, Yf, Yt = makeYbus(baseMVA, bus, branch)
|
156
|
+
|
157
|
+
# try to select an interior initial point
|
158
|
+
ll = xmin.copy()
|
159
|
+
uu = xmax.copy()
|
160
|
+
ll[xmin == -inf] = -2e19 # replace Inf with numerical proxies
|
161
|
+
uu[xmax == inf] = 2e19
|
162
|
+
x0 = (ll + uu) / 2
|
163
|
+
Varefs = bus[bus[:, IDX.bus.BUS_TYPE] == IDX.bus.REF, IDX.bus.VA] * deg2rad
|
164
|
+
x0[vv['i1']['Va']:vv['iN']['Va']] = Varefs[0] # angles set to first reference angle
|
165
|
+
if ny > 0:
|
166
|
+
ipwl = find(gencost[:, IDX.cost.MODEL] == IDX.cost.PW_LINEAR)
|
167
|
+
# PQ = np.r_[gen[:, PMAX], gen[:, QMAX]]
|
168
|
+
# c = totcost(gencost[ipwl, :], PQ[ipwl])
|
169
|
+
# largest y-value in CCV data
|
170
|
+
c = gencost.flatten('F')[putils.sub2ind(np.shape(gencost), ipwl,
|
171
|
+
IDX.cost.NCOST + 2 * gencost[ipwl, IDX.cost.NCOST])]
|
172
|
+
x0[vv['i1']['y']:vv['iN']['y']] = max(c) + 0.1 * np.abs(max(c))
|
173
|
+
# x0[vv['i1']['y']:vv['iN']['y']) = c + 0.1 * np.abs(c)
|
174
|
+
|
175
|
+
# find branches with flow limits
|
176
|
+
il = find((branch[:, IDX.branch.RATE_A] != 0) & (branch[:, IDX.branch.RATE_A] < 1e10))
|
177
|
+
nl2 = len(il) # number of constrained lines
|
178
|
+
|
179
|
+
# ----- run opf -----
|
180
|
+
# build Jacobian and Hessian structure
|
181
|
+
if A is not None and sp.issparse(A):
|
182
|
+
nA = A.shape[0] # number of original linear constraints
|
183
|
+
else:
|
184
|
+
nA = 0
|
185
|
+
nx = len(x0)
|
186
|
+
f = branch[:, IDX.branch.F_BUS] # list of "from" buses
|
187
|
+
t = branch[:, IDX.branch.T_BUS] # list of "to" buses
|
188
|
+
Cf = c_sparse((np.ones(nl), (np.arange(nl), f)), (nl, nb)) # connection matrix for line & from buses
|
189
|
+
Ct = c_sparse((np.ones(nl), (np.arange(nl), t)), (nl, nb)) # connection matrix for line & to buses
|
190
|
+
Cl = Cf + Ct
|
191
|
+
Cb = Cl.T * Cl + sp.eye(nb, nb)
|
192
|
+
Cl2 = Cl[il, :]
|
193
|
+
Cg = c_sparse((np.ones(ng), (gen[:, IDX.gen.GEN_BUS], np.arange(ng))), (nb, ng))
|
194
|
+
nz = nx - 2 * (nb + ng)
|
195
|
+
nxtra = nx - 2 * nb
|
196
|
+
if nz > 0:
|
197
|
+
Js = sp.vstack([
|
198
|
+
sp.hstack([Cb, Cb, Cg, c_sparse((nb, ng)), c_sparse((nb, nz))]),
|
199
|
+
sp.hstack([Cb, Cb, c_sparse((nb, ng)), Cg, c_sparse((nb, nz))]),
|
200
|
+
sp.hstack([Cl2, Cl2, c_sparse((nl2, 2 * ng)), c_sparse((nl2, nz))]),
|
201
|
+
sp.hstack([Cl2, Cl2, c_sparse((nl2, 2 * ng)), c_sparse((nl2, nz))])
|
202
|
+
], 'coo')
|
203
|
+
else:
|
204
|
+
Js = sp.vstack([
|
205
|
+
sp.hstack([Cb, Cb, Cg, c_sparse((nb, ng))]),
|
206
|
+
sp.hstack([Cb, Cb, c_sparse((nb, ng)), Cg,]),
|
207
|
+
sp.hstack([Cl2, Cl2, c_sparse((nl2, 2 * ng)),]),
|
208
|
+
sp.hstack([Cl2, Cl2, c_sparse((nl2, 2 * ng)),])
|
209
|
+
], 'coo')
|
210
|
+
|
211
|
+
if A is not None and sp.issparse(A):
|
212
|
+
Js = sp.vstack([Js, A], 'coo')
|
213
|
+
|
214
|
+
f, _, d2f = opf_costfcn(x0, om, True)
|
215
|
+
Hs = sp.tril(d2f + sp.vstack([
|
216
|
+
sp.hstack([Cb, Cb, c_sparse((nb, nxtra))]),
|
217
|
+
sp.hstack([Cb, Cb, c_sparse((nb, nxtra))]),
|
218
|
+
c_sparse((nxtra, nx))
|
219
|
+
]), format='coo')
|
220
|
+
|
221
|
+
# set options struct for IPOPT
|
222
|
+
# options = {}
|
223
|
+
# options['ipopt'] = ipopt_options([], ppopt)
|
224
|
+
|
225
|
+
# extra data to pass to functions
|
226
|
+
userdata = dict(om=om, Ybus=Ybus, Yf=Yf, Yt=Yt, ppopt=ppopt,
|
227
|
+
il=il, A=A, nA=nA,
|
228
|
+
neqnln=2 * nb, niqnln=2 * nl2, Js=Js, Hs=Hs)
|
229
|
+
|
230
|
+
# check Jacobian and Hessian structure
|
231
|
+
# xr = rand(x0.shape)
|
232
|
+
# lmbda = rand( 2 * nb + 2 * nl2)
|
233
|
+
# Js1 = eval_jac_g(x, flag, userdata) #(xr, options.auxdata)
|
234
|
+
# Hs1 = eval_h(xr, 1, lmbda, userdata)
|
235
|
+
# i1, j1, s = find(Js)
|
236
|
+
# i2, j2, s = find(Js1)
|
237
|
+
# if (len(i1) != len(i2)) | (norm(i1 - i2) != 0) | (norm(j1 - j2) != 0):
|
238
|
+
# raise ValueError, 'something''s wrong with the Jacobian structure'
|
239
|
+
#
|
240
|
+
# i1, j1, s = find(Hs)
|
241
|
+
# i2, j2, s = find(Hs1)
|
242
|
+
# if (len(i1) != len(i2)) | (norm(i1 - i2) != 0) | (norm(j1 - j2) != 0):
|
243
|
+
# raise ValueError, 'something''s wrong with the Hessian structure'
|
244
|
+
|
245
|
+
# define variable and constraint bounds
|
246
|
+
# n is the number of variables
|
247
|
+
n = x0.shape[0]
|
248
|
+
# xl is the lower bound of x as bounded constraints
|
249
|
+
xl = xmin
|
250
|
+
# xu is the upper bound of x as bounded constraints
|
251
|
+
xu = xmax
|
252
|
+
|
253
|
+
neqnln = 2 * nb
|
254
|
+
niqnln = 2 * nl2
|
255
|
+
|
256
|
+
# number of constraints
|
257
|
+
m = neqnln + niqnln + nA
|
258
|
+
# lower bound of constraint
|
259
|
+
gl = np.r_[np.zeros(neqnln), -inf * np.ones(niqnln), l]
|
260
|
+
# upper bound of constraints
|
261
|
+
gu = np.r_[np.zeros(neqnln), np.zeros(niqnln), u]
|
262
|
+
|
263
|
+
# number of nonzeros in Jacobi matrix
|
264
|
+
nnzj = Js.nnz
|
265
|
+
# number of non-zeros in Hessian matrix, you can set it to 0
|
266
|
+
nnzh = Hs.nnz
|
267
|
+
|
268
|
+
eval_hessian = True
|
269
|
+
if eval_hessian:
|
270
|
+
def hessian(x, lagrange, obj_factor, flag, user_data=None): return \
|
271
|
+
eval_h(x, lagrange, obj_factor, flag, userdata)
|
272
|
+
|
273
|
+
nlp = pyipopt.create(n, xl, xu, m, gl, gu, nnzj, nnzh,
|
274
|
+
eval_f, eval_grad_f, eval_g, eval_jac_g, hessian)
|
275
|
+
else:
|
276
|
+
nnzh = 0
|
277
|
+
nlp = pyipopt.create(n, xl, xu, m, gl, gu, nnzj, nnzh,
|
278
|
+
eval_f, eval_grad_f, eval_g, eval_jac_g)
|
279
|
+
|
280
|
+
nlp.int_option('print_level', 5)
|
281
|
+
nlp.num_option('tol', 1.0000e-12)
|
282
|
+
nlp.int_option('max_iter', 250)
|
283
|
+
nlp.num_option('dual_inf_tol', 0.10000)
|
284
|
+
nlp.num_option('constr_viol_tol', 1.0000e-06)
|
285
|
+
nlp.num_option('compl_inf_tol', 1.0000e-05)
|
286
|
+
nlp.num_option('acceptable_tol', 1.0000e-08)
|
287
|
+
nlp.num_option('acceptable_constr_viol_tol', 1.0000e-04)
|
288
|
+
nlp.num_option('acceptable_compl_inf_tol', 0.0010000)
|
289
|
+
nlp.str_option('mu_strategy', 'adaptive')
|
290
|
+
|
291
|
+
iter = 0
|
292
|
+
|
293
|
+
def intermediate_callback(algmod, iter_count, obj_value, inf_pr, inf_du,
|
294
|
+
mu, d_norm, regularization_size, alpha_du, alpha_pr, ls_trials,
|
295
|
+
user_data=None):
|
296
|
+
iter = iter_count
|
297
|
+
return True
|
298
|
+
|
299
|
+
nlp.set_intermediate_callback(intermediate_callback)
|
300
|
+
|
301
|
+
# run the optimization
|
302
|
+
# returns final solution x, upper and lower bound for multiplier, final
|
303
|
+
# objective function obj and the return status of ipopt
|
304
|
+
x, zl, zu, obj, status, zg = nlp.solve(x0, m, userdata)
|
305
|
+
|
306
|
+
info = dict(x=x, zl=zl, zu=zu, obj=obj, status=status, lmbda=zg)
|
307
|
+
|
308
|
+
nlp.close()
|
309
|
+
|
310
|
+
success = (status == 0) | (status == 1)
|
311
|
+
|
312
|
+
output = {'iterations': iter}
|
313
|
+
|
314
|
+
f, _ = opf_costfcn(x, om)
|
315
|
+
|
316
|
+
# update solution data
|
317
|
+
Va = x[vv['i1']['Va']:vv['iN']['Va']]
|
318
|
+
Vm = x[vv['i1']['Vm']:vv['iN']['Vm']]
|
319
|
+
Pg = x[vv['i1']['Pg']:vv['iN']['Pg']]
|
320
|
+
Qg = x[vv['i1']['Qg']:vv['iN']['Qg']]
|
321
|
+
V = Vm * np.exp(1j * Va)
|
322
|
+
|
323
|
+
# ----- calculate return values -----
|
324
|
+
# update voltages & generator outputs
|
325
|
+
bus[:, IDX.bus.VA] = Va * rad2deg
|
326
|
+
bus[:, IDX.bus.VM] = Vm
|
327
|
+
gen[:, IDX.gen.PG] = Pg * baseMVA
|
328
|
+
gen[:, IDX.gen.QG] = Qg * baseMVA
|
329
|
+
gen[:, IDX.gen.VG] = Vm[gen[:, IDX.gen.GEN_BUS].astype(int)]
|
330
|
+
|
331
|
+
# compute branch flows
|
332
|
+
f_br = branch[:, IDX.branch.F_BUS].astype(int)
|
333
|
+
t_br = branch[:, IDX.branch.T_BUS].astype(int)
|
334
|
+
Sf = V[f_br] * np.conj(Yf * V) # cplx pwr at "from" bus, p.u.
|
335
|
+
St = V[t_br] * np.conj(Yt * V) # cplx pwr at "to" bus, p.u.
|
336
|
+
branch[:, IDX.branch.PF] = Sf.real * baseMVA
|
337
|
+
branch[:, IDX.branch.QF] = Sf.imag * baseMVA
|
338
|
+
branch[:, IDX.branch.PT] = St.real * baseMVA
|
339
|
+
branch[:, IDX.branch.QT] = St.imag * baseMVA
|
340
|
+
|
341
|
+
# line constraint is actually on square of limit
|
342
|
+
# so we must fix multipliers
|
343
|
+
muSf = np.zeros(nl)
|
344
|
+
muSt = np.zeros(nl)
|
345
|
+
if len(il) > 0:
|
346
|
+
muSf[il] = 2 * info['lmbda'][2 * nb + np.arange(nl2)] * branch[il, IDX.branch.RATE_A] / baseMVA
|
347
|
+
muSt[il] = 2 * info['lmbda'][2 * nb + nl2 + np.arange(nl2)] * branch[il, IDX.branch.RATE_A] / baseMVA
|
348
|
+
|
349
|
+
# update Lagrange multipliers
|
350
|
+
bus[:, IDX.bus.MU_VMAX] = info['zu'][vv['i1']['Vm']:vv['iN']['Vm']]
|
351
|
+
bus[:, IDX.bus.MU_VMIN] = info['zl'][vv['i1']['Vm']:vv['iN']['Vm']]
|
352
|
+
gen[:, IDX.gen.MU_PMAX] = info['zu'][vv['i1']['Pg']:vv['iN']['Pg']] / baseMVA
|
353
|
+
gen[:, IDX.gen.MU_PMIN] = info['zl'][vv['i1']['Pg']:vv['iN']['Pg']] / baseMVA
|
354
|
+
gen[:, IDX.gen.MU_QMAX] = info['zu'][vv['i1']['Qg']:vv['iN']['Qg']] / baseMVA
|
355
|
+
gen[:, IDX.gen.MU_QMIN] = info['zl'][vv['i1']['Qg']:vv['iN']['Qg']] / baseMVA
|
356
|
+
bus[:, IDX.bus.LAM_P] = info['lmbda'][nn['i1']['Pmis']:nn['iN']['Pmis']] / baseMVA
|
357
|
+
bus[:, IDX.bus.LAM_Q] = info['lmbda'][nn['i1']['Qmis']:nn['iN']['Qmis']] / baseMVA
|
358
|
+
branch[:, IDX.branch.MU_SF] = muSf / baseMVA
|
359
|
+
branch[:, IDX.branch.MU_ST] = muSt / baseMVA
|
360
|
+
|
361
|
+
# package up results
|
362
|
+
nlnN = om.getN('nln')
|
363
|
+
|
364
|
+
# extract multipliers for nonlinear constraints
|
365
|
+
kl = find(info['lmbda'][:2 * nb] < 0)
|
366
|
+
ku = find(info['lmbda'][:2 * nb] > 0)
|
367
|
+
nl_mu_l = np.zeros(nlnN)
|
368
|
+
nl_mu_u = np.r_[np.zeros(2 * nb), muSf, muSt]
|
369
|
+
nl_mu_l[kl] = -info['lmbda'][kl]
|
370
|
+
nl_mu_u[ku] = info['lmbda'][ku]
|
371
|
+
|
372
|
+
# extract multipliers for linear constraints
|
373
|
+
lam_lin = info['lmbda'][2 * nb + 2 * nl2 + np.arange(nA)] # lmbda for linear constraints
|
374
|
+
kl = find(lam_lin < 0) # lower bound binding
|
375
|
+
ku = find(lam_lin > 0) # upper bound binding
|
376
|
+
mu_l = np.zeros(nA)
|
377
|
+
mu_l[kl] = -lam_lin[kl]
|
378
|
+
mu_u = np.zeros(nA)
|
379
|
+
mu_u[ku] = lam_lin[ku]
|
380
|
+
|
381
|
+
mu = {
|
382
|
+
'var': {'l': info['zl'], 'u': info['zu']},
|
383
|
+
'nln': {'l': nl_mu_l, 'u': nl_mu_u},
|
384
|
+
'lin': {'l': mu_l, 'u': mu_u}
|
385
|
+
}
|
386
|
+
|
387
|
+
results = ppc
|
388
|
+
results['bus'], results['branch'], results['gen'], \
|
389
|
+
results['om'], results['x'], results['mu'], results['f'] = \
|
390
|
+
bus, branch, gen, om, x, mu, f
|
391
|
+
|
392
|
+
pimul = np.r_[
|
393
|
+
results['mu']['nln']['l'] - results['mu']['nln']['u'],
|
394
|
+
results['mu']['lin']['l'] - results['mu']['lin']['u'],
|
395
|
+
-np.ones(ny > 0),
|
396
|
+
results['mu']['var']['l'] - results['mu']['var']['u']
|
397
|
+
]
|
398
|
+
raw = {'xr': x, 'pimul': pimul, 'info': info['status'], 'output': output}
|
399
|
+
|
400
|
+
return results, success, raw
|
401
|
+
|
402
|
+
|
403
|
+
def eval_f(x, user_data=None):
|
404
|
+
"""
|
405
|
+
Calculates the objective value.
|
406
|
+
|
407
|
+
@param x: input vector
|
408
|
+
"""
|
409
|
+
om = user_data['om']
|
410
|
+
f, _ = opf_costfcn(x, om)
|
411
|
+
return f
|
412
|
+
|
413
|
+
|
414
|
+
def eval_grad_f(x, user_data=None):
|
415
|
+
"""
|
416
|
+
Calculates gradient for objective function.
|
417
|
+
"""
|
418
|
+
om = user_data['om']
|
419
|
+
_, df = opf_costfcn(x, om)
|
420
|
+
return df
|
421
|
+
|
422
|
+
|
423
|
+
def eval_g(x, user_data=None):
|
424
|
+
"""
|
425
|
+
Calculates the constraint values and returns an array.
|
426
|
+
"""
|
427
|
+
om = user_data['om']
|
428
|
+
Ybus = user_data['Ybus']
|
429
|
+
Yf = user_data['Yf']
|
430
|
+
Yt = user_data['Yt']
|
431
|
+
ppopt = user_data['ppopt']
|
432
|
+
il = user_data['il']
|
433
|
+
A = user_data['A']
|
434
|
+
|
435
|
+
hn, gn, _, _ = opf_consfcn(x, om, Ybus, Yf, Yt, ppopt, il)
|
436
|
+
|
437
|
+
if A is not None and sp.issparse(A):
|
438
|
+
c = np.r_[gn, hn, A * x]
|
439
|
+
else:
|
440
|
+
c = np.r_[gn, hn]
|
441
|
+
return c
|
442
|
+
|
443
|
+
|
444
|
+
def eval_jac_g(x, flag, user_data=None):
|
445
|
+
"""
|
446
|
+
Calculates the Jacobi matrix.
|
447
|
+
|
448
|
+
If the flag is true, returns a tuple (row, col) to indicate the
|
449
|
+
sparse Jacobi matrix's structure.
|
450
|
+
If the flag is false, returns the values of the Jacobi matrix
|
451
|
+
with length nnzj.
|
452
|
+
"""
|
453
|
+
Js = user_data['Js']
|
454
|
+
if flag:
|
455
|
+
return (Js.row, Js.col)
|
456
|
+
else:
|
457
|
+
om = user_data['om']
|
458
|
+
Ybus = user_data['Ybus']
|
459
|
+
Yf = user_data['Yf']
|
460
|
+
Yt = user_data['Yt']
|
461
|
+
ppopt = user_data['ppopt']
|
462
|
+
il = user_data['il']
|
463
|
+
A = user_data['A']
|
464
|
+
|
465
|
+
_, _, dhn, dgn = opf_consfcn(x, om, Ybus, Yf, Yt, ppopt, il)
|
466
|
+
|
467
|
+
if A is not None and sp.issparse(A):
|
468
|
+
J = sp.vstack([dgn.T, dhn.T, A], 'coo')
|
469
|
+
else:
|
470
|
+
J = sp.vstack([dgn.T, dhn.T], 'coo')
|
471
|
+
|
472
|
+
# FIXME: Extend PyIPOPT to handle changes in sparsity structure
|
473
|
+
nnzj = Js.nnz
|
474
|
+
Jd = np.zeros(nnzj)
|
475
|
+
Jc = J.tocsc()
|
476
|
+
for i in range(nnzj):
|
477
|
+
Jd[i] = Jc[Js.row[i], Js.col[i]]
|
478
|
+
|
479
|
+
return Jd
|
480
|
+
|
481
|
+
|
482
|
+
def eval_h(x, lagrange, obj_factor, flag, user_data=None):
|
483
|
+
"""
|
484
|
+
Calculates the Hessian matrix (optional).
|
485
|
+
|
486
|
+
If omitted, set nnzh to 0 and Ipopt will use approximated Hessian
|
487
|
+
which will make the convergence slower.
|
488
|
+
"""
|
489
|
+
Hs = user_data['Hs']
|
490
|
+
if flag:
|
491
|
+
return (Hs.row, Hs.col)
|
492
|
+
else:
|
493
|
+
neqnln = user_data['neqnln']
|
494
|
+
niqnln = user_data['niqnln']
|
495
|
+
om = user_data['om']
|
496
|
+
Ybus = user_data['Ybus']
|
497
|
+
Yf = user_data['Yf']
|
498
|
+
Yt = user_data['Yt']
|
499
|
+
ppopt = user_data['ppopt']
|
500
|
+
il = user_data['il']
|
501
|
+
|
502
|
+
lam = {}
|
503
|
+
lam['eqnonlin'] = lagrange[:neqnln]
|
504
|
+
lam['ineqnonlin'] = lagrange[np.arange(niqnln) + neqnln]
|
505
|
+
|
506
|
+
H = opf_hessfcn(x, lam, om, Ybus, Yf, Yt, ppopt, il, obj_factor)
|
507
|
+
|
508
|
+
Hl = sp.tril(H, format='csc')
|
509
|
+
|
510
|
+
# FIXME: Extend PyIPOPT to handle changes in sparsity structure
|
511
|
+
nnzh = Hs.nnz
|
512
|
+
Hd = np.zeros(nnzh)
|
513
|
+
for i in range(nnzh):
|
514
|
+
Hd[i] = Hl[Hs.row[i], Hs.col[i]]
|
515
|
+
|
516
|
+
return Hd
|
517
|
+
|
518
|
+
|
519
|
+
def qps_cplex(H, c, A, l, u, xmin, xmax, x0, opt):
|
520
|
+
"""Quadratic Program Solver based on CPLEX.
|
521
|
+
|
522
|
+
A wrapper function providing a PYPOWER standardized interface for using
|
523
|
+
C{cplexqp} or C{cplexlp} to solve the following QP (quadratic programming)
|
524
|
+
problem::
|
525
|
+
|
526
|
+
min 1/2 X'*H*x + c'*x
|
527
|
+
x
|
528
|
+
|
529
|
+
subject to::
|
530
|
+
|
531
|
+
l <= A*x <= u (linear constraints)
|
532
|
+
xmin <= x <= xmax (variable bounds)
|
533
|
+
|
534
|
+
Inputs (all optional except C{H}, C{c}, C{A} and C{l}):
|
535
|
+
- C{H} : matrix (possibly sparse) of quadratic cost coefficients
|
536
|
+
- C{c} : vector of linear cost coefficients
|
537
|
+
- C{A, l, u} : define the optional linear constraints. Default
|
538
|
+
values for the elements of L and U are -Inf and Inf, respectively.
|
539
|
+
- C{xmin, xmax} : optional lower and upper bounds on the
|
540
|
+
C{x} variables, defaults are -Inf and Inf, respectively.
|
541
|
+
- C{x0} : optional starting value of optimization vector C{x}
|
542
|
+
- C{opt} : optional options structure with the following fields,
|
543
|
+
all of which are also optional (default values shown in parentheses)
|
544
|
+
- C{verbose} (0) - controls level of progress output displayed
|
545
|
+
- 0 = no progress output
|
546
|
+
- 1 = some progress output
|
547
|
+
- 2 = verbose progress output
|
548
|
+
- C{cplex_opt} - options dict for CPLEX, value in
|
549
|
+
verbose overrides these options
|
550
|
+
- C{problem} : The inputs can alternatively be supplied in a single
|
551
|
+
C{problem} dict with fields corresponding to the input arguments
|
552
|
+
described above: C{H, c, A, l, u, xmin, xmax, x0, opt}
|
553
|
+
|
554
|
+
Outputs:
|
555
|
+
- C{x} : solution vector
|
556
|
+
- C{f} : final objective function value
|
557
|
+
- C{exitflag} : CPLEXQP/CPLEXLP exit flag
|
558
|
+
(see C{cplexqp} and C{cplexlp} documentation for details)
|
559
|
+
- C{output} : CPLEXQP/CPLEXLP output dict
|
560
|
+
(see C{cplexqp} and C{cplexlp} documentation for details)
|
561
|
+
- C{lmbda} : dict containing the Langrange and Kuhn-Tucker
|
562
|
+
multipliers on the constraints, with fields:
|
563
|
+
- mu_l - lower (left-hand) limit on linear constraints
|
564
|
+
- mu_u - upper (right-hand) limit on linear constraints
|
565
|
+
- lower - lower bound on optimization variables
|
566
|
+
- upper - upper bound on optimization variables
|
567
|
+
|
568
|
+
@author: Ray Zimmerman (PSERC Cornell)
|
569
|
+
"""
|
570
|
+
# ----- input argument handling -----
|
571
|
+
# gather inputs
|
572
|
+
if isinstance(H, dict): # problem struct
|
573
|
+
p = H
|
574
|
+
if 'opt' in p:
|
575
|
+
opt = p['opt']
|
576
|
+
if 'x0' in p:
|
577
|
+
x0 = p['x0']
|
578
|
+
if 'xmax' in p:
|
579
|
+
xmax = p['xmax']
|
580
|
+
if 'xmin' in p:
|
581
|
+
xmin = p['xmin']
|
582
|
+
if 'u' in p:
|
583
|
+
u = p['u']
|
584
|
+
if 'l' in p:
|
585
|
+
l = p['l']
|
586
|
+
if 'A' in p:
|
587
|
+
A = p['A']
|
588
|
+
if 'c' in p:
|
589
|
+
c = p['c']
|
590
|
+
if 'H' in p:
|
591
|
+
H = p['H']
|
592
|
+
else: # individual args
|
593
|
+
assert H is not None
|
594
|
+
assert c is not None
|
595
|
+
assert A is not None
|
596
|
+
assert l is not None
|
597
|
+
|
598
|
+
if opt is None:
|
599
|
+
opt = {}
|
600
|
+
# if x0 is None:
|
601
|
+
# x0 = np.array([])
|
602
|
+
# if xmax is None:
|
603
|
+
# xmax = np.array([])
|
604
|
+
# if xmin is None:
|
605
|
+
# xmin = np.array([])
|
606
|
+
|
607
|
+
# define nx, set default values for missing optional inputs
|
608
|
+
if len(H) == 0 or not np.any(np.any(H)):
|
609
|
+
if len(A) == 0 and len(xmin) == 0 and len(xmax) == 0:
|
610
|
+
logger.debug('qps_cplex: LP problem must include constraints or variable bounds\n')
|
611
|
+
else:
|
612
|
+
if len(A) > 0:
|
613
|
+
nx = np.shape(A)[1]
|
614
|
+
elif len(xmin) > 0:
|
615
|
+
nx = len(xmin)
|
616
|
+
else: # if len(xmax) > 0
|
617
|
+
nx = len(xmax)
|
618
|
+
else:
|
619
|
+
nx = np.shape(H)[0]
|
620
|
+
|
621
|
+
if len(c) == 0:
|
622
|
+
c = np.zeros(nx)
|
623
|
+
|
624
|
+
if len(A) > 0 and (len(l) == 0 or all(l == -inf)) and \
|
625
|
+
(len(u) == 0 or all(u == inf)):
|
626
|
+
A = None # no limits => no linear constraints
|
627
|
+
|
628
|
+
nA = np.shape(A)[0] # number of original linear constraints
|
629
|
+
if len(u) == 0: # By default, linear inequalities are ...
|
630
|
+
u = inf * np.ones(nA) # ... unbounded above and ...
|
631
|
+
|
632
|
+
if len(l) == 0:
|
633
|
+
l = -inf * np.ones(nA) # ... unbounded below.
|
634
|
+
|
635
|
+
if len(xmin) == 0: # By default, optimization variables are ...
|
636
|
+
xmin = -inf * np.ones(nx) # ... unbounded below and ...
|
637
|
+
|
638
|
+
if len(xmax) == 0:
|
639
|
+
xmax = inf * np.ones(nx) # ... unbounded above.
|
640
|
+
|
641
|
+
if len(x0) == 0:
|
642
|
+
x0 = np.zeros(nx)
|
643
|
+
|
644
|
+
# default options
|
645
|
+
if 'verbose' in opt:
|
646
|
+
verbose = opt['verbose']
|
647
|
+
else:
|
648
|
+
verbose = 0
|
649
|
+
|
650
|
+
# if 'max_it' in opt:
|
651
|
+
# max_it = opt['max_it']
|
652
|
+
# else:
|
653
|
+
# max_it = 0
|
654
|
+
|
655
|
+
# split up linear constraints
|
656
|
+
ieq = find(np.abs(u-l) <= putils.EPS) # equality
|
657
|
+
igt = find(u >= 1e10 & l > -1e10) # greater than, unbounded above
|
658
|
+
ilt = find(l <= -1e10 & u < 1e10) # less than, unbounded below
|
659
|
+
ibx = find((np.abs(u-l) > putils.EPS) & (u < 1e10) & (l > -1e10))
|
660
|
+
Ae = A[ieq, :]
|
661
|
+
be = u[ieq]
|
662
|
+
Ai = np.r_[A[ilt, :], -A[igt, :], A[ibx, :] - A[ibx, :]]
|
663
|
+
bi = np.r_[u[ilt], -l[igt], u[ibx], -l[ibx]]
|
664
|
+
|
665
|
+
# grab some dimensions
|
666
|
+
nlt = len(ilt) # number of upper bounded linear inequalities
|
667
|
+
ngt = len(igt) # number of lower bounded linear inequalities
|
668
|
+
nbx = len(ibx) # number of doubly bounded linear inequalities
|
669
|
+
|
670
|
+
# set up options struct for CPLEX
|
671
|
+
if 'cplex_opt' in opt:
|
672
|
+
cplex_opt = cplex_options(opt['cplex_opt'])
|
673
|
+
else:
|
674
|
+
cplex_opt = cplex_options
|
675
|
+
|
676
|
+
cplex = Cplex('null')
|
677
|
+
vstr = cplex.getVersion
|
678
|
+
s, e, tE, m, t = re.compile(vstr, '(\d+\.\d+)\.')
|
679
|
+
vnum = int(t[0][0])
|
680
|
+
vrb = max([0, verbose - 1])
|
681
|
+
cplex_opt['barrier']['display'] = vrb
|
682
|
+
cplex_opt['conflict']['display'] = vrb
|
683
|
+
cplex_opt['mip']['display'] = vrb
|
684
|
+
cplex_opt['sifting']['display'] = vrb
|
685
|
+
cplex_opt['simplex']['display'] = vrb
|
686
|
+
cplex_opt['tune']['display'] = vrb
|
687
|
+
if vrb and (vnum > 12.2):
|
688
|
+
cplex_opt['diagnostics'] = 'on'
|
689
|
+
# if max_it:
|
690
|
+
# cplex_opt. ## not sure what to set here
|
691
|
+
|
692
|
+
if len(Ai) == 0 and len(Ae) == 0:
|
693
|
+
unconstrained = 1
|
694
|
+
Ae = c_sparse((1, nx))
|
695
|
+
be = 0
|
696
|
+
else:
|
697
|
+
unconstrained = 0
|
698
|
+
|
699
|
+
# call the solver
|
700
|
+
if verbose:
|
701
|
+
methods = [
|
702
|
+
'default',
|
703
|
+
'primal simplex',
|
704
|
+
'dual simplex',
|
705
|
+
'network simplex',
|
706
|
+
'barrier',
|
707
|
+
'sifting',
|
708
|
+
'concurrent'
|
709
|
+
]
|
710
|
+
|
711
|
+
if len(H) == 0 or not np.any(np.any(H)):
|
712
|
+
logger.info('CPLEX Version %s -- %s LP solver\n' %
|
713
|
+
(vstr, methods[cplex_opt['lpmethod'] + 1]))
|
714
|
+
|
715
|
+
x, f, eflag, output, lam = \
|
716
|
+
cplexlp(c, Ai, bi, Ae, be, xmin, xmax, x0, cplex_opt)
|
717
|
+
else:
|
718
|
+
logger.info('CPLEX Version %s -- %s QP solver\n' %
|
719
|
+
(vstr, methods[cplex_opt['qpmethod'] + 1]))
|
720
|
+
# ensure H is numerically symmetric
|
721
|
+
if H != H.T:
|
722
|
+
H = (H + H.T) / 2
|
723
|
+
|
724
|
+
x, f, eflag, output, lam = \
|
725
|
+
cplexqp(H, c, Ai, bi, Ae, be, xmin, xmax, x0, cplex_opt)
|
726
|
+
|
727
|
+
# check for empty results (in case optimization failed)
|
728
|
+
if len(x) == 0:
|
729
|
+
x = nan * np.zeros(nx)
|
730
|
+
|
731
|
+
if len(f) == 0:
|
732
|
+
f = nan
|
733
|
+
|
734
|
+
if len(lam) == 0:
|
735
|
+
lam['ineqlin'] = nan * np.zeros(len(bi))
|
736
|
+
lam['eqlin'] = nan * np.zeros(len(be))
|
737
|
+
lam['lower'] = nan * np.zeros(nx)
|
738
|
+
lam['upper'] = nan * np.zeros(nx)
|
739
|
+
mu_l = nan * np.zeros(nA)
|
740
|
+
mu_u = nan * np.zeros(nA)
|
741
|
+
else:
|
742
|
+
mu_l = np.zeros(nA)
|
743
|
+
mu_u = np.zeros(nA)
|
744
|
+
|
745
|
+
if unconstrained:
|
746
|
+
lam['eqlin'] = np.array([])
|
747
|
+
|
748
|
+
# negate prices depending on version
|
749
|
+
if vnum < 12.3:
|
750
|
+
lam['eqlin'] = -lam['eqlin']
|
751
|
+
lam['ineqlin'] = -lam['ineqlin']
|
752
|
+
|
753
|
+
# repackage lambdas
|
754
|
+
kl = find(lam.eqlin < 0) # lower bound binding
|
755
|
+
ku = find(lam.eqlin > 0) # upper bound binding
|
756
|
+
|
757
|
+
mu_l[ieq[kl]] = -lam['eqlin'][kl]
|
758
|
+
mu_l[igt] = lam['ineqlin'][nlt + np.ones(ngt)]
|
759
|
+
mu_l[ibx] = lam['ineqlin'][nlt + ngt + nbx + np.ones(nbx)]
|
760
|
+
|
761
|
+
mu_u[ieq[ku]] = lam['eqlin'][ku]
|
762
|
+
mu_u[ilt] = lam['ineqlin'][:nlt]
|
763
|
+
mu_u[ibx] = lam['ineqlin'][nlt + ngt + np.ones(nbx)]
|
764
|
+
|
765
|
+
lmbda = dict(mu_l=mu_l, mu_u=mu_u, lower=lam.lower, upper=lam.upper)
|
766
|
+
|
767
|
+
return x, f, eflag, output, lmbda
|
768
|
+
|
769
|
+
|
770
|
+
@require_gurobi
|
771
|
+
def qps_gurobi(H, c, A, l, u, xmin, xmax, x0, opt):
|
772
|
+
"""
|
773
|
+
Quadratic Program Solver based on GUROBI.
|
774
|
+
|
775
|
+
A wrapper function providing a PYPOWER standardized interface for using
|
776
|
+
gurobipy to solve the following QP (quadratic programming)
|
777
|
+
problem:
|
778
|
+
|
779
|
+
min 1/2 x'*H*x + c'*x
|
780
|
+
x
|
781
|
+
|
782
|
+
subject to
|
783
|
+
|
784
|
+
l <= A*x <= u (linear constraints)
|
785
|
+
xmin <= x <= xmax (variable bounds)
|
786
|
+
|
787
|
+
Inputs (all optional except H, c, A and l):
|
788
|
+
H : matrix (possibly sparse) of quadratic cost coefficients
|
789
|
+
c : vector of linear cost coefficients
|
790
|
+
A, l, u : define the optional linear constraints. Default
|
791
|
+
values for the elements of l and u are -Inf and Inf,
|
792
|
+
respectively.
|
793
|
+
xmin, xmax : optional lower and upper bounds on the
|
794
|
+
C{x} variables, defaults are -Inf and Inf, respectively.
|
795
|
+
x0 : optional starting value of optimization vector C{x}
|
796
|
+
opt : optional options structure with the following fields,
|
797
|
+
all of which are also optional (default values shown in
|
798
|
+
parentheses)
|
799
|
+
verbose (0) - controls level of progress output displayed
|
800
|
+
0 = no progress output
|
801
|
+
1 = some progress output
|
802
|
+
2 = verbose progress output
|
803
|
+
grb_opt - options dict for Gurobi, value in
|
804
|
+
verbose overrides these options
|
805
|
+
problem : The inputs can alternatively be supplied in a single
|
806
|
+
PROBLEM dict with fields corresponding to the input arguments
|
807
|
+
described above: H, c, A, l, u, xmin, xmax, x0, opt
|
808
|
+
|
809
|
+
Outputs:
|
810
|
+
x : solution vector
|
811
|
+
f : final objective function value
|
812
|
+
exitflag : gurobipy exit flag
|
813
|
+
1 = converged
|
814
|
+
0 or negative values = negative of GUROBI_MEX exit flag
|
815
|
+
(see gurobipy documentation for details)
|
816
|
+
output : gurobipy output dict
|
817
|
+
(see gurobipy documentation for details)
|
818
|
+
lmbda : dict containing the Langrange and Kuhn-Tucker
|
819
|
+
multipliers on the constraints, with fields:
|
820
|
+
mu_l - lower (left-hand) limit on linear constraints
|
821
|
+
mu_u - upper (right-hand) limit on linear constraints
|
822
|
+
lower - lower bound on optimization variables
|
823
|
+
upper - upper bound on optimization variables
|
824
|
+
|
825
|
+
Note the calling syntax is almost identical to that of QUADPROG
|
826
|
+
from MathWorks' Optimization Toolbox. The main difference is that
|
827
|
+
the linear constraints are specified with A, l, u instead of
|
828
|
+
A, b, Aeq, beq.
|
829
|
+
|
830
|
+
Calling syntax options:
|
831
|
+
x, f, exitflag, output, lmbda = ...
|
832
|
+
qps_gurobi(H, c, A, l, u, xmin, xmax, x0, opt)
|
833
|
+
|
834
|
+
r = qps_gurobi(H, c, A, l, u)
|
835
|
+
r = qps_gurobi(H, c, A, l, u, xmin, xmax)
|
836
|
+
r = qps_gurobi(H, c, A, l, u, xmin, xmax, x0)
|
837
|
+
r = qps_gurobi(H, c, A, l, u, xmin, xmax, x0, opt)
|
838
|
+
r = qps_gurobi(problem), where problem is a dict with fields:
|
839
|
+
H, c, A, l, u, xmin, xmax, x0, opt
|
840
|
+
all fields except 'c', 'A' and 'l' or 'u' are optional
|
841
|
+
|
842
|
+
Example: (problem from from http://www.jmu.edu/docs/sasdoc/sashtml/iml/chap8/sect12.htm)
|
843
|
+
H = [ 1003.1 4.3 6.3 5.9;
|
844
|
+
4.3 2.2 2.1 3.9;
|
845
|
+
6.3 2.1 3.5 4.8;
|
846
|
+
5.9 3.9 4.8 10 ]
|
847
|
+
c = np.zeros((4, 1))
|
848
|
+
A = [ [1 1 1 1]
|
849
|
+
[0.17 0.11 0.10 0.18] ]
|
850
|
+
l = [1; 0.10]
|
851
|
+
u = [1; Inf]
|
852
|
+
xmin = np.zeros((4, 1))
|
853
|
+
x0 = [1; 0; 0; 1]
|
854
|
+
opt = {'verbose': 2}
|
855
|
+
x, f, s, out, lmbda = qps_gurobi(H, c, A, l, u, xmin, [], x0, opt)
|
856
|
+
|
857
|
+
@see: L{gurobipy}.
|
858
|
+
"""
|
859
|
+
# ----- input argument handling -----
|
860
|
+
# gather inputs
|
861
|
+
if isinstance(H, dict): # problem struct
|
862
|
+
p = H
|
863
|
+
if 'opt' in p:
|
864
|
+
opt = p['opt']
|
865
|
+
if 'x0' in p:
|
866
|
+
x0 = p['x0']
|
867
|
+
if 'xmax' in p:
|
868
|
+
xmax = p['xmax']
|
869
|
+
if 'xmin' in p:
|
870
|
+
xmin = p['xmin']
|
871
|
+
if 'u' in p:
|
872
|
+
u = p['u']
|
873
|
+
if 'l' in p:
|
874
|
+
l = p['l']
|
875
|
+
if 'A' in p:
|
876
|
+
A = p['A']
|
877
|
+
if 'c' in p:
|
878
|
+
c = p['c']
|
879
|
+
if 'H' in p:
|
880
|
+
H = p['H']
|
881
|
+
else: # individual args
|
882
|
+
assert H is not None
|
883
|
+
assert c is not None
|
884
|
+
assert A is not None
|
885
|
+
assert l is not None
|
886
|
+
|
887
|
+
if opt is None:
|
888
|
+
opt = {}
|
889
|
+
# if x0 is None:
|
890
|
+
# x0 = np.array([])
|
891
|
+
# if xmax is None:
|
892
|
+
# xmax = np.array([])
|
893
|
+
# if xmin is None:
|
894
|
+
# xmin = np.array([])
|
895
|
+
|
896
|
+
# define nx, set default values for missing optional inputs
|
897
|
+
if len(H) == 0 or not np.any(np.any(H)):
|
898
|
+
if len(A) == 0 and len(xmin) == 0 and len(xmax) == 0:
|
899
|
+
logger.debug('qps_gurobi: LP problem must include constraints or variable bounds\n')
|
900
|
+
else:
|
901
|
+
if len(A) > 0:
|
902
|
+
nx = np.shape(A)[1]
|
903
|
+
elif len(xmin) > 0:
|
904
|
+
nx = len(xmin)
|
905
|
+
else: # if len(xmax) > 0
|
906
|
+
nx = len(xmax)
|
907
|
+
H = c_sparse((nx, nx))
|
908
|
+
else:
|
909
|
+
nx = np.shape(H)[0]
|
910
|
+
|
911
|
+
if len(c) == 0:
|
912
|
+
c = np.zeros(nx)
|
913
|
+
|
914
|
+
if len(A) > 0 and (len(l) == 0 or all(l == -inf)) and \
|
915
|
+
(len(u) == 0 or all(u == inf)):
|
916
|
+
A = None # no limits => no linear constraints
|
917
|
+
|
918
|
+
nA = np.shape(A)[0] # number of original linear constraints
|
919
|
+
if nA:
|
920
|
+
if len(u) == 0: # By default, linear inequalities are ...
|
921
|
+
u = inf * np.ones(nA) # ... unbounded above and ...
|
922
|
+
|
923
|
+
if len(l) == 0:
|
924
|
+
l = -inf * np.ones(nA) # ... unbounded below.
|
925
|
+
|
926
|
+
if len(x0) == 0:
|
927
|
+
x0 = np.zeros(nx)
|
928
|
+
|
929
|
+
# default options
|
930
|
+
if 'verbose' in opt:
|
931
|
+
verbose = opt['verbose']
|
932
|
+
else:
|
933
|
+
verbose = 0
|
934
|
+
|
935
|
+
# if 'max_it' in opt:
|
936
|
+
# max_it = opt['max_it']
|
937
|
+
# else:
|
938
|
+
# max_it = 0
|
939
|
+
|
940
|
+
# set up options struct for Gurobi
|
941
|
+
if 'grb_opt' in opt:
|
942
|
+
g_opt = gurobi_options(opt['grb_opt'])
|
943
|
+
else:
|
944
|
+
g_opt = gurobi_options()
|
945
|
+
|
946
|
+
g_opt['Display'] = min(verbose, 3)
|
947
|
+
if verbose:
|
948
|
+
g_opt['DisplayInterval'] = 1
|
949
|
+
else:
|
950
|
+
g_opt['DisplayInterval'] = inf
|
951
|
+
|
952
|
+
if not sp.issparse(A):
|
953
|
+
A = c_sparse(A)
|
954
|
+
|
955
|
+
# split up linear constraints
|
956
|
+
ieq = find(np.abs(u-l) <= putils.EPS) # equality
|
957
|
+
igt = find(u >= 1e10 & l > -1e10) # greater than, unbounded above
|
958
|
+
ilt = find(l <= -1e10 & u < 1e10) # less than, unbounded below
|
959
|
+
ibx = find((np.abs(u-l) > putils.EPS) & (u < 1e10) & (l > -1e10))
|
960
|
+
|
961
|
+
# grab some dimensions
|
962
|
+
nlt = len(ilt) # number of upper bounded linear inequalities
|
963
|
+
ngt = len(igt) # number of lower bounded linear inequalities
|
964
|
+
nbx = len(ibx) # number of doubly bounded linear inequalities
|
965
|
+
neq = len(ieq) # number of equalities
|
966
|
+
niq = nlt + ngt + 2 * nbx # number of inequalities
|
967
|
+
|
968
|
+
AA = [A[ieq, :], A[ilt, :], -A[igt, :], A[ibx, :], -A[ibx, :]]
|
969
|
+
bb = [u[ieq], u[ilt], -l[igt], u[ibx], -l[ibx]]
|
970
|
+
contypes = '=' * neq + '<' * niq
|
971
|
+
|
972
|
+
# call the solver
|
973
|
+
if len(H) == 0 or not np.any(np.any(H)):
|
974
|
+
lpqp = 'LP'
|
975
|
+
else:
|
976
|
+
lpqp = 'QP'
|
977
|
+
rr, cc, vv = find(H)
|
978
|
+
g_opt['QP']['qrow'] = int(rr.T - 1)
|
979
|
+
g_opt['QP']['qcol'] = int(cc.T - 1)
|
980
|
+
g_opt['QP']['qval'] = 0.5 * vv.T
|
981
|
+
|
982
|
+
if verbose:
|
983
|
+
methods = [
|
984
|
+
'primal simplex',
|
985
|
+
'dual simplex',
|
986
|
+
'interior point',
|
987
|
+
'concurrent',
|
988
|
+
'deterministic concurrent'
|
989
|
+
]
|
990
|
+
logger.info('Gurobi Version %s -- %s %s solver\n'
|
991
|
+
'<unknown>' % (methods[g_opt['Method'] + 1], lpqp))
|
992
|
+
|
993
|
+
x, f, eflag, output, lmbda = \
|
994
|
+
gurobipy(c.T, 1, AA, bb, contypes, xmin, xmax, 'C', g_opt)
|
995
|
+
pi = lmbda['Pi']
|
996
|
+
rc = lmbda['RC']
|
997
|
+
output['flag'] = eflag
|
998
|
+
if eflag == 2:
|
999
|
+
eflag = 1 # optimal solution found
|
1000
|
+
else:
|
1001
|
+
eflag = -eflag # failed somehow
|
1002
|
+
|
1003
|
+
# check for empty results (in case optimization failed)
|
1004
|
+
lam = {}
|
1005
|
+
if len(x) == 0:
|
1006
|
+
x = nan(nx, 1)
|
1007
|
+
lam['lower'] = nan(nx)
|
1008
|
+
lam['upper'] = nan(nx)
|
1009
|
+
else:
|
1010
|
+
lam['lower'] = np.zeros(nx)
|
1011
|
+
lam['upper'] = np.zeros(nx)
|
1012
|
+
|
1013
|
+
if len(f) == 0:
|
1014
|
+
f = nan
|
1015
|
+
|
1016
|
+
if len(pi) == 0:
|
1017
|
+
pi = nan(len(bb))
|
1018
|
+
|
1019
|
+
kl = find(rc > 0) # lower bound binding
|
1020
|
+
ku = find(rc < 0) # upper bound binding
|
1021
|
+
lam['lower'][kl] = rc[kl]
|
1022
|
+
lam['upper'][ku] = -rc[ku]
|
1023
|
+
lam['eqlin'] = pi[:neq + 1]
|
1024
|
+
lam['ineqlin'] = pi[neq + range(niq + 1)]
|
1025
|
+
mu_l = np.zeros(nA)
|
1026
|
+
mu_u = np.zeros(nA)
|
1027
|
+
|
1028
|
+
# repackage lmbdas
|
1029
|
+
kl = find(lam['eqlin'] > 0) # lower bound binding
|
1030
|
+
ku = find(lam['eqlin'] < 0) # upper bound binding
|
1031
|
+
|
1032
|
+
mu_l[ieq[kl]] = lam['eqlin'][kl]
|
1033
|
+
mu_l[igt] = -lam['ineqlin'][nlt + range(ngt + 1)]
|
1034
|
+
mu_l[ibx] = -lam['ineqlin'][nlt + ngt + nbx + range(nbx)]
|
1035
|
+
|
1036
|
+
mu_u[ieq[ku]] = -lam['eqlin'][ku]
|
1037
|
+
mu_u[ilt] = -lam['ineqlin'][:nlt + 1]
|
1038
|
+
mu_u[ibx] = -lam['ineqlin'][nlt + ngt + range(nbx + 1)]
|
1039
|
+
|
1040
|
+
lmbda = dict(mu_l=mu_l, mu_u=mu_u, lower=lam['lower'], upper=lam['upper'])
|
1041
|
+
|
1042
|
+
return x, f, eflag, output, lmbda
|
1043
|
+
|
1044
|
+
|
1045
|
+
def qps_ipopt(H, c, A, l, u, xmin, xmax, x0, opt):
|
1046
|
+
"""
|
1047
|
+
Quadratic Program Solver based on IPOPT.
|
1048
|
+
|
1049
|
+
Uses IPOPT to solve the following QP (quadratic programming) problem::
|
1050
|
+
|
1051
|
+
min 1/2 x'*H*x + c'*x
|
1052
|
+
x
|
1053
|
+
|
1054
|
+
subject to::
|
1055
|
+
|
1056
|
+
l <= A*x <= u (linear constraints)
|
1057
|
+
xmin <= x <= xmax (variable bounds)
|
1058
|
+
|
1059
|
+
Inputs (all optional except C{H}, C{C}, C{A} and C{L}):
|
1060
|
+
- C{H} : matrix (possibly sparse) of quadratic cost coefficients
|
1061
|
+
- C{C} : vector of linear cost coefficients
|
1062
|
+
- C{A, l, u} : define the optional linear constraints. Default
|
1063
|
+
values for the elements of C{l} and C{u} are -Inf and Inf,
|
1064
|
+
respectively.
|
1065
|
+
- C{xmin, xmax} : optional lower and upper bounds on the
|
1066
|
+
C{x} variables, defaults are -Inf and Inf, respectively.
|
1067
|
+
- C{x0} : optional starting value of optimization vector C{x}
|
1068
|
+
- C{opt} : optional options structure with the following fields,
|
1069
|
+
all of which are also optional (default values shown in parentheses)
|
1070
|
+
- C{verbose} (0) - controls level of progress output displayed
|
1071
|
+
- 0 = no progress output
|
1072
|
+
- 1 = some progress output
|
1073
|
+
- 2 = verbose progress output
|
1074
|
+
- C{max_it} (0) - maximum number of iterations allowed
|
1075
|
+
- 0 = use algorithm default
|
1076
|
+
- C{ipopt_opt} - options struct for IPOPT, values in
|
1077
|
+
C{verbose} and C{max_it} override these options
|
1078
|
+
- C{problem} : The inputs can alternatively be supplied in a single
|
1079
|
+
C{problem} dict with fields corresponding to the input arguments
|
1080
|
+
described above: C{H, c, A, l, u, xmin, xmax, x0, opt}
|
1081
|
+
|
1082
|
+
Outputs:
|
1083
|
+
- C{x} : solution vector
|
1084
|
+
- C{f} : final objective function value
|
1085
|
+
- C{exitflag} : exit flag
|
1086
|
+
- 1 = first order optimality conditions satisfied
|
1087
|
+
- 0 = maximum number of iterations reached
|
1088
|
+
- -1 = numerically failed
|
1089
|
+
- C{output} : output struct with the following fields:
|
1090
|
+
- C{iterations} - number of iterations performed
|
1091
|
+
- C{hist} - dict list with trajectories of the following:
|
1092
|
+
C{feascond}, C{gradcond}, C{compcond}, C{costcond}, C{gamma},
|
1093
|
+
C{stepsize}, C{obj}, C{alphap}, C{alphad}
|
1094
|
+
- message - exit message
|
1095
|
+
- C{lmbda} : dict containing the Langrange and Kuhn-Tucker
|
1096
|
+
multipliers on the constraints, with fields:
|
1097
|
+
- C{mu_l} - lower (left-hand) limit on linear constraints
|
1098
|
+
- C{mu_u} - upper (right-hand) limit on linear constraints
|
1099
|
+
- C{lower} - lower bound on optimization variables
|
1100
|
+
- C{upper} - upper bound on optimization variables
|
1101
|
+
|
1102
|
+
Calling syntax options::
|
1103
|
+
x, f, exitflag, output, lmbda = \
|
1104
|
+
qps_ipopt(H, c, A, l, u, xmin, xmax, x0, opt)
|
1105
|
+
|
1106
|
+
x = qps_ipopt(H, c, A, l, u)
|
1107
|
+
x = qps_ipopt(H, c, A, l, u, xmin, xmax)
|
1108
|
+
x = qps_ipopt(H, c, A, l, u, xmin, xmax, x0)
|
1109
|
+
x = qps_ipopt(H, c, A, l, u, xmin, xmax, x0, opt)
|
1110
|
+
x = qps_ipopt(problem), where problem is a struct with fields:
|
1111
|
+
H, c, A, l, u, xmin, xmax, x0, opt
|
1112
|
+
all fields except 'c', 'A' and 'l' or 'u' are optional
|
1113
|
+
x = qps_ipopt(...)
|
1114
|
+
x, f = qps_ipopt(...)
|
1115
|
+
x, f, exitflag = qps_ipopt(...)
|
1116
|
+
x, f, exitflag, output = qps_ipopt(...)
|
1117
|
+
x, f, exitflag, output, lmbda = qps_ipopt(...)
|
1118
|
+
|
1119
|
+
Example::
|
1120
|
+
H = [ 1003.1 4.3 6.3 5.9;
|
1121
|
+
4.3 2.2 2.1 3.9;
|
1122
|
+
6.3 2.1 3.5 4.8;
|
1123
|
+
5.9 3.9 4.8 10 ]
|
1124
|
+
c = np.zeros((4, 1))
|
1125
|
+
A = [ 1 1 1 1
|
1126
|
+
0.17 0.11 0.10 0.18 ]
|
1127
|
+
l = [1, 0.10]
|
1128
|
+
u = [1, Inf]
|
1129
|
+
xmin = np.zeros((4, 1))
|
1130
|
+
x0 = [1, 0, 0, 1]
|
1131
|
+
opt = {'verbose': 2)
|
1132
|
+
x, f, s, out, lambda = qps_ipopt(H, c, A, l, u, xmin, [], x0, opt)
|
1133
|
+
|
1134
|
+
Problem from U{http://www.jmu.edu/docs/sasdoc/sashtml/iml/chap8/sect12.htm}
|
1135
|
+
|
1136
|
+
@see: C{pyipopt}, L{ipopt_options}
|
1137
|
+
|
1138
|
+
@author: Ray Zimmerman (PSERC Cornell)
|
1139
|
+
"""
|
1140
|
+
# ----- input argument handling -----
|
1141
|
+
# gather inputs
|
1142
|
+
if isinstance(H, dict): # problem struct
|
1143
|
+
p = H
|
1144
|
+
if 'opt' in p:
|
1145
|
+
opt = p['opt']
|
1146
|
+
if 'x0' in p:
|
1147
|
+
x0 = p['x0']
|
1148
|
+
if 'xmax' in p:
|
1149
|
+
xmax = p['xmax']
|
1150
|
+
if 'xmin' in p:
|
1151
|
+
xmin = p['xmin']
|
1152
|
+
if 'u' in p:
|
1153
|
+
u = p['u']
|
1154
|
+
if 'l' in p:
|
1155
|
+
l = p['l']
|
1156
|
+
if 'A' in p:
|
1157
|
+
A = p['A']
|
1158
|
+
if 'c' in p:
|
1159
|
+
c = p['c']
|
1160
|
+
if 'H' in p:
|
1161
|
+
H = p['H']
|
1162
|
+
else: # individual args
|
1163
|
+
assert H is not None
|
1164
|
+
assert c is not None
|
1165
|
+
assert A is not None
|
1166
|
+
assert l is not None
|
1167
|
+
|
1168
|
+
if opt is None:
|
1169
|
+
opt = {}
|
1170
|
+
# if x0 is None:
|
1171
|
+
# x0 = np.array([])
|
1172
|
+
# if xmax is None:
|
1173
|
+
# xmax = np.array([])
|
1174
|
+
# if xmin is None:
|
1175
|
+
# xmin = np.array([])
|
1176
|
+
|
1177
|
+
# define nx, set default values for missing optional inputs
|
1178
|
+
if len(H) == 0 or not np.any(np.any(H)):
|
1179
|
+
if len(A) == 0 and len(xmin) == 0 and len(xmax) == 0:
|
1180
|
+
logger.info('qps_ipopt: LP problem must include constraints or variable bounds')
|
1181
|
+
else:
|
1182
|
+
if len(A) > 0:
|
1183
|
+
nx = np.shape(A)[1]
|
1184
|
+
elif len(xmin) > 0:
|
1185
|
+
nx = len(xmin)
|
1186
|
+
else: # if len(xmax) > 0
|
1187
|
+
nx = len(xmax)
|
1188
|
+
H = c_sparse((nx, nx))
|
1189
|
+
else:
|
1190
|
+
nx = np.shape(H)[0]
|
1191
|
+
|
1192
|
+
if len(c) == 0:
|
1193
|
+
c = np.zeros(nx)
|
1194
|
+
|
1195
|
+
if len(A) > 0 and (len(l) == 0 or all(l == -inf)) and \
|
1196
|
+
(len(u) == 0 or all(u == inf)):
|
1197
|
+
A = None # no limits => no linear constraints
|
1198
|
+
|
1199
|
+
nA = np.shape(A)[0] # number of original linear constraints
|
1200
|
+
if nA:
|
1201
|
+
if len(u) == 0: # By default, linear inequalities are ...
|
1202
|
+
u = inf * np.ones(nA) # ... unbounded above and ...
|
1203
|
+
|
1204
|
+
if len(l) == 0:
|
1205
|
+
l = -inf * np.ones(nA) # ... unbounded below.
|
1206
|
+
|
1207
|
+
if len(x0) == 0:
|
1208
|
+
x0 = np.zeros(nx)
|
1209
|
+
|
1210
|
+
# default options
|
1211
|
+
if 'verbose' in opt:
|
1212
|
+
verbose = opt['verbose']
|
1213
|
+
else:
|
1214
|
+
verbose = 0
|
1215
|
+
|
1216
|
+
if 'max_it' in opt:
|
1217
|
+
max_it = opt['max_it']
|
1218
|
+
else:
|
1219
|
+
max_it = 0
|
1220
|
+
|
1221
|
+
# make sure args are sparse/full as expected by IPOPT
|
1222
|
+
if len(H) > 0:
|
1223
|
+
if not sp.issparse(H):
|
1224
|
+
H = c_sparse(H)
|
1225
|
+
|
1226
|
+
if not sp.issparse(A):
|
1227
|
+
A = c_sparse(A)
|
1228
|
+
|
1229
|
+
# ----- run optimization -----
|
1230
|
+
# set options dict for IPOPT
|
1231
|
+
options = {}
|
1232
|
+
if 'ipopt_opt' in opt:
|
1233
|
+
options['ipopt'] = ipopt_options(opt['ipopt_opt'])
|
1234
|
+
else:
|
1235
|
+
options['ipopt'] = ipopt_options()
|
1236
|
+
|
1237
|
+
options['ipopt']['jac_c_constant'] = 'yes'
|
1238
|
+
options['ipopt']['jac_d_constant'] = 'yes'
|
1239
|
+
options['ipopt']['hessian_constant'] = 'yes'
|
1240
|
+
options['ipopt']['least_square_init_primal'] = 'yes'
|
1241
|
+
options['ipopt']['least_square_init_duals'] = 'yes'
|
1242
|
+
# options['ipopt']['mehrotra_algorithm'] = 'yes' ## default 'no'
|
1243
|
+
if verbose:
|
1244
|
+
options['ipopt']['print_level'] = min(12, verbose * 2 + 1)
|
1245
|
+
else:
|
1246
|
+
options['ipopt']['print_level = 0']
|
1247
|
+
|
1248
|
+
if max_it:
|
1249
|
+
options['ipopt']['max_iter'] = max_it
|
1250
|
+
|
1251
|
+
# define variable and constraint bounds, if given
|
1252
|
+
if nA:
|
1253
|
+
options['cu'] = u
|
1254
|
+
options['cl'] = l
|
1255
|
+
|
1256
|
+
if len(xmin) > 0:
|
1257
|
+
options['lb'] = xmin
|
1258
|
+
|
1259
|
+
if len(xmax) > 0:
|
1260
|
+
options['ub'] = xmax
|
1261
|
+
|
1262
|
+
# assign function handles
|
1263
|
+
funcs = {}
|
1264
|
+
funcs['objective'] = lambda x: 0.5 * x.T * H * x + c.T * x
|
1265
|
+
funcs['gradient'] = lambda x: H * x + c
|
1266
|
+
funcs['constraints'] = lambda x: A * x
|
1267
|
+
funcs['jacobian'] = lambda x: A
|
1268
|
+
funcs['jacobianstructure'] = lambda: A
|
1269
|
+
funcs['hessian'] = lambda x, sigma, lmbda: np.tril(H)
|
1270
|
+
funcs['hessianstructure'] = lambda: np.tril(H)
|
1271
|
+
|
1272
|
+
# run the optimization
|
1273
|
+
x, info = pyipopt(x0, funcs, options)
|
1274
|
+
|
1275
|
+
if info['status'] == 0 | info['status'] == 1:
|
1276
|
+
eflag = 1
|
1277
|
+
else:
|
1278
|
+
eflag = 0
|
1279
|
+
|
1280
|
+
output = {}
|
1281
|
+
if 'iter' in info:
|
1282
|
+
output['iterations'] = info['iter']
|
1283
|
+
|
1284
|
+
output['info'] = info['status']
|
1285
|
+
f = funcs['objective'](x)
|
1286
|
+
|
1287
|
+
# repackage lmbdas
|
1288
|
+
kl = find(info['lmbda'] < 0) # lower bound binding
|
1289
|
+
ku = find(info['lmbda'] > 0) # upper bound binding
|
1290
|
+
mu_l = np.zeros(nA)
|
1291
|
+
mu_l[kl] = -info['lmbda'][kl]
|
1292
|
+
mu_u = np.zeros(nA)
|
1293
|
+
mu_u[ku] = info['lmbda'][ku]
|
1294
|
+
|
1295
|
+
lmbda = dict(mu_l=mu_l, mu_u=mu_u, lower=info['zl'], upper=info['zu'])
|
1296
|
+
|
1297
|
+
return x, f, eflag, output, lmbda
|
1298
|
+
|
1299
|
+
|
1300
|
+
@require_mosek
|
1301
|
+
def qps_mosek(H, c=None, A=None, l=None, u=None, xmin=None, xmax=None,
|
1302
|
+
x0=None, opt=None):
|
1303
|
+
"""
|
1304
|
+
Quadratic Program Solver based on MOSEK.
|
1305
|
+
|
1306
|
+
A wrapper function providing a PYPOWER standardized interface for using
|
1307
|
+
MOSEKOPT to solve the following QP (quadratic programming) problem::
|
1308
|
+
|
1309
|
+
min 1/2 x'*H*x + c'*x
|
1310
|
+
x
|
1311
|
+
|
1312
|
+
subject to::
|
1313
|
+
|
1314
|
+
l <= A*x <= u (linear constraints)
|
1315
|
+
xmin <= x <= xmax (variable bounds)
|
1316
|
+
|
1317
|
+
Inputs (all optional except C{H}, C{C}, C{A} and C{L}):
|
1318
|
+
- C{H} : matrix (possibly sparse) of quadratic cost coefficients
|
1319
|
+
- C{C} : vector of linear cost coefficients
|
1320
|
+
- C{A, l, u} : define the optional linear constraints. Default
|
1321
|
+
values for the elements of L and U are -Inf and Inf, respectively.
|
1322
|
+
- xmin, xmax : optional lower and upper bounds on the
|
1323
|
+
C{x} variables, defaults are -Inf and Inf, respectively.
|
1324
|
+
- C{x0} : optional starting value of optimization vector C{x}
|
1325
|
+
- C{opt} : optional options structure with the following fields,
|
1326
|
+
all of which are also optional (default values shown in parentheses)
|
1327
|
+
- C{verbose} (0) - controls level of progress output displayed
|
1328
|
+
- 0 = no progress output
|
1329
|
+
- 1 = some progress output
|
1330
|
+
- 2 = verbose progress output
|
1331
|
+
- C{max_it} (0) - maximum number of iterations allowed
|
1332
|
+
- 0 = use algorithm default
|
1333
|
+
- C{mosek_opt} - options struct for MOSEK, values in
|
1334
|
+
C{verbose} and C{max_it} override these options
|
1335
|
+
- C{problem} : The inputs can alternatively be supplied in a single
|
1336
|
+
C{problem} struct with fields corresponding to the input arguments
|
1337
|
+
described above: C{H, c, A, l, u, xmin, xmax, x0, opt}
|
1338
|
+
|
1339
|
+
Outputs:
|
1340
|
+
- C{x} : solution vector
|
1341
|
+
- C{f} : final objective function value
|
1342
|
+
- C{exitflag} : exit flag
|
1343
|
+
- 1 = success
|
1344
|
+
- 0 = terminated at maximum number of iterations
|
1345
|
+
- -1 = primal or dual infeasible
|
1346
|
+
< 0 = the negative of the MOSEK return code
|
1347
|
+
- C{output} : output dict with the following fields:
|
1348
|
+
- C{r} - MOSEK return code
|
1349
|
+
- C{res} - MOSEK result dict
|
1350
|
+
- C{lmbda} : dict containing the Langrange and Kuhn-Tucker
|
1351
|
+
multipliers on the constraints, with fields:
|
1352
|
+
- C{mu_l} - lower (left-hand) limit on linear constraints
|
1353
|
+
- C{mu_u} - upper (right-hand) limit on linear constraints
|
1354
|
+
- C{lower} - lower bound on optimization variables
|
1355
|
+
- C{upper} - upper bound on optimization variables
|
1356
|
+
|
1357
|
+
@author: Ray Zimmerman (PSERC Cornell)
|
1358
|
+
"""
|
1359
|
+
# ----- input argument handling -----
|
1360
|
+
# gather inputs
|
1361
|
+
if isinstance(H, dict): # problem struct
|
1362
|
+
p = H
|
1363
|
+
else: # individual args
|
1364
|
+
p = {'H': H, 'c': c, 'A': A, 'l': l, 'u': u}
|
1365
|
+
if xmin is not None:
|
1366
|
+
p['xmin'] = xmin
|
1367
|
+
if xmax is not None:
|
1368
|
+
p['xmax'] = xmax
|
1369
|
+
if x0 is not None:
|
1370
|
+
p['x0'] = x0
|
1371
|
+
if opt is not None:
|
1372
|
+
p['opt'] = opt
|
1373
|
+
|
1374
|
+
# define nx, set default values for H and c
|
1375
|
+
if 'H' not in p or len(p['H']) or not np.any(np.any(p['H'])):
|
1376
|
+
if ('A' not in p) | len(p['A']) == 0 & \
|
1377
|
+
('xmin' not in p) | len(p['xmin']) == 0 & \
|
1378
|
+
('xmax' not in p) | len(p['xmax']) == 0:
|
1379
|
+
logger.debug('qps_mosek: LP problem must include constraints or variable bounds\n')
|
1380
|
+
else:
|
1381
|
+
if 'A' in p & len(p['A']) > 0:
|
1382
|
+
nx = np.shape(p['A'])[1]
|
1383
|
+
elif 'xmin' in p & len(p['xmin']) > 0:
|
1384
|
+
nx = len(p['xmin'])
|
1385
|
+
else: # if isfield(p, 'xmax') && ~isempty(p.xmax)
|
1386
|
+
nx = len(p['xmax'])
|
1387
|
+
p['H'] = c_sparse((nx, nx))
|
1388
|
+
qp = 0
|
1389
|
+
else:
|
1390
|
+
nx = np.shape(p['H'])[0]
|
1391
|
+
qp = 1
|
1392
|
+
|
1393
|
+
if 'c' not in p | len(p['c']) == 0:
|
1394
|
+
p['c'] = np.zeros(nx)
|
1395
|
+
|
1396
|
+
if 'x0' not in p | len(p['x0']) == 0:
|
1397
|
+
p['x0'] = np.zeros(nx)
|
1398
|
+
|
1399
|
+
# default options
|
1400
|
+
if 'opt' not in p:
|
1401
|
+
p['opt'] = []
|
1402
|
+
|
1403
|
+
if 'verbose' in p['opt']:
|
1404
|
+
verbose = p['opt']['verbose']
|
1405
|
+
else:
|
1406
|
+
verbose = 0
|
1407
|
+
|
1408
|
+
if 'max_it' in p['opt']:
|
1409
|
+
max_it = p['opt']['max_it']
|
1410
|
+
else:
|
1411
|
+
max_it = 0
|
1412
|
+
|
1413
|
+
if 'mosek_opt' in p['opt']:
|
1414
|
+
mosek_opt = mosek_options(p['opt']['mosek_opt'])
|
1415
|
+
else:
|
1416
|
+
mosek_opt = mosek_options()
|
1417
|
+
|
1418
|
+
if max_it:
|
1419
|
+
mosek_opt['MSK_IPAR_INTPNT_MAX_ITERATIONS'] = max_it
|
1420
|
+
|
1421
|
+
if qp:
|
1422
|
+
mosek_opt['MSK_IPAR_OPTIMIZER'] = 0 # default solver only for QP
|
1423
|
+
|
1424
|
+
# set up problem struct for MOSEK
|
1425
|
+
prob = {}
|
1426
|
+
prob['c'] = p['c']
|
1427
|
+
if qp:
|
1428
|
+
prob['qosubi'], prob['qosubj'], prob['qoval'] = find(sp.tril(c_sparse(p['H'])))
|
1429
|
+
|
1430
|
+
if 'A' in p & len(p['A']) > 0:
|
1431
|
+
prob['a'] = c_sparse(p['A'])
|
1432
|
+
|
1433
|
+
if 'l' in p & len(p['A']) > 0:
|
1434
|
+
prob['blc'] = p['l']
|
1435
|
+
|
1436
|
+
if 'u' in p & len(p['A']) > 0:
|
1437
|
+
prob['buc'] = p['u']
|
1438
|
+
|
1439
|
+
if 'xmin' in p & len(p['xmin']) > 0:
|
1440
|
+
prob['blx'] = p['xmin']
|
1441
|
+
|
1442
|
+
if 'xmax' in p & len(p['xmax']) > 0:
|
1443
|
+
prob['bux'] = p['xmax']
|
1444
|
+
|
1445
|
+
# A is not allowed to be empty
|
1446
|
+
if 'a' not in prob | len(prob['a']) == 0:
|
1447
|
+
unconstrained = True
|
1448
|
+
prob['a'] = c_sparse((1, (1, 1)), (1, nx))
|
1449
|
+
prob.blc = -inf
|
1450
|
+
prob.buc = inf
|
1451
|
+
else:
|
1452
|
+
unconstrained = False
|
1453
|
+
|
1454
|
+
# ----- run optimization -----
|
1455
|
+
if verbose:
|
1456
|
+
methods = [
|
1457
|
+
'default',
|
1458
|
+
'interior point',
|
1459
|
+
'<default>',
|
1460
|
+
'<default>',
|
1461
|
+
'primal simplex',
|
1462
|
+
'dual simplex',
|
1463
|
+
'primal dual simplex',
|
1464
|
+
'automatic simplex',
|
1465
|
+
'<default>',
|
1466
|
+
'<default>',
|
1467
|
+
'concurrent'
|
1468
|
+
]
|
1469
|
+
if len(H) == 0 or not np.any(np.any(H)):
|
1470
|
+
lpqp = 'LP'
|
1471
|
+
else:
|
1472
|
+
lpqp = 'QP'
|
1473
|
+
|
1474
|
+
# (this code is also in mpver.m)
|
1475
|
+
# MOSEK Version 6.0.0.93 (Build date: 2010-10-26 13:03:27)
|
1476
|
+
# MOSEK Version 6.0.0.106 (Build date: 2011-3-17 10:46:54)
|
1477
|
+
# pat = 'Version (\.*\d)+.*Build date: (\d\d\d\d-\d\d-\d\d)';
|
1478
|
+
pat = 'Version (\.*\d)+.*Build date: (\d+-\d+-\d+)'
|
1479
|
+
s, e, tE, m, t = re.compile(eval('mosekopt'), pat)
|
1480
|
+
if len(t) == 0:
|
1481
|
+
vn = '<unknown>'
|
1482
|
+
else:
|
1483
|
+
vn = t[0][0]
|
1484
|
+
|
1485
|
+
logger.info('MOSEK Version %s -- %s %s solver\n' %
|
1486
|
+
(vn, methods[mosek_opt['MSK_IPAR_OPTIMIZER'] + 1], lpqp))
|
1487
|
+
|
1488
|
+
cmd = 'minimize echo(%d)' % verbose
|
1489
|
+
r, res = mosekopt(cmd, prob, mosek_opt)
|
1490
|
+
|
1491
|
+
# ----- repackage results -----
|
1492
|
+
if 'sol' in res:
|
1493
|
+
if 'bas' in res['sol']:
|
1494
|
+
sol = res['sol.bas']
|
1495
|
+
else:
|
1496
|
+
sol = res['sol.itr']
|
1497
|
+
x = sol['xx']
|
1498
|
+
else:
|
1499
|
+
sol = np.array([])
|
1500
|
+
x = np.array([])
|
1501
|
+
|
1502
|
+
# ----- process return codes -----
|
1503
|
+
if 'symbcon' in res:
|
1504
|
+
sc = res['symbcon']
|
1505
|
+
else:
|
1506
|
+
r2, res2 = mosekopt('symbcon echo(0)')
|
1507
|
+
sc = res2['symbcon']
|
1508
|
+
|
1509
|
+
eflag = -r
|
1510
|
+
msg = ''
|
1511
|
+
if r == sc.MSK_RES_OK:
|
1512
|
+
if len(sol) > 0:
|
1513
|
+
# if sol['solsta'] == sc.MSK_SOL_STA_OPTIMAL:
|
1514
|
+
if sol['solsta'] == 'OPTIMAL':
|
1515
|
+
msg = 'The solution is optimal.'
|
1516
|
+
eflag = 1
|
1517
|
+
else:
|
1518
|
+
eflag = -1
|
1519
|
+
# if sol['prosta'] == sc['MSK_PRO_STA_PRIM_INFEAS']:
|
1520
|
+
if sol['prosta'] == 'PRIMAL_INFEASIBLE':
|
1521
|
+
msg = 'The problem is primal infeasible.'
|
1522
|
+
# elif sol['prosta'] == sc['MSK_PRO_STA_DUAL_INFEAS']:
|
1523
|
+
elif sol['prosta'] == 'DUAL_INFEASIBLE':
|
1524
|
+
msg = 'The problem is dual infeasible.'
|
1525
|
+
else:
|
1526
|
+
msg = sol['solsta']
|
1527
|
+
|
1528
|
+
elif r == sc['MSK_RES_TRM_MAX_ITERATIONS']:
|
1529
|
+
eflag = 0
|
1530
|
+
msg = 'The optimizer terminated at the maximum number of iterations.'
|
1531
|
+
else:
|
1532
|
+
if 'rmsg' in res and 'rcodestr' in res:
|
1533
|
+
msg = '%s : %s' % (res['rcodestr'], res['rmsg'])
|
1534
|
+
else:
|
1535
|
+
msg = 'MOSEK return code = %d' % r
|
1536
|
+
|
1537
|
+
# always alert user if license is expired
|
1538
|
+
if (verbose or r == 1001) and len(msg) < 0:
|
1539
|
+
logger.info('%s\n' % msg)
|
1540
|
+
|
1541
|
+
# ----- repackage results -----
|
1542
|
+
if r == 0:
|
1543
|
+
f = p['c'].T * x
|
1544
|
+
if len(p['H']) > 0:
|
1545
|
+
f = 0.5 * x.T * p['H'] * x + f
|
1546
|
+
else:
|
1547
|
+
f = np.array([])
|
1548
|
+
|
1549
|
+
output = {}
|
1550
|
+
output['r'] = r
|
1551
|
+
output['res'] = res
|
1552
|
+
|
1553
|
+
if 'sol' in res:
|
1554
|
+
lmbda = {}
|
1555
|
+
lmbda['lower'] = sol['slx']
|
1556
|
+
lmbda['upper'] = sol['sux']
|
1557
|
+
lmbda['mu_l'] = sol['slc']
|
1558
|
+
lmbda['mu_u'] = sol['suc']
|
1559
|
+
if unconstrained:
|
1560
|
+
lmbda['mu_l'] = np.array([])
|
1561
|
+
lmbda['mu_u'] = np.array([])
|
1562
|
+
else:
|
1563
|
+
lmbda = np.array([])
|
1564
|
+
|
1565
|
+
return x, f, eflag, output, lmbda
|
1566
|
+
|
1567
|
+
|
1568
|
+
def qps_pips(H, c, A, l, u, xmin=None, xmax=None, x0=None, opt=None):
|
1569
|
+
"""
|
1570
|
+
Uses the Python Interior Point Solver (PIPS) to solve the following
|
1571
|
+
QP (quadratic programming) problem::
|
1572
|
+
|
1573
|
+
min 1/2 x'*H*x + C'*x
|
1574
|
+
x
|
1575
|
+
|
1576
|
+
subject to::
|
1577
|
+
|
1578
|
+
l <= A*x <= u (linear constraints)
|
1579
|
+
xmin <= x <= xmax (variable bounds)
|
1580
|
+
|
1581
|
+
Note the calling syntax is almost identical to that of QUADPROG from
|
1582
|
+
MathWorks' Optimization Toolbox. The main difference is that the linear
|
1583
|
+
constraints are specified with C{A}, C{L}, C{U} instead of C{A}, C{B},
|
1584
|
+
C{Aeq}, C{Beq}.
|
1585
|
+
|
1586
|
+
Example from U{http://www.uc.edu/sashtml/iml/chap8/sect12.htm}:
|
1587
|
+
|
1588
|
+
>>> from numpy import array, zeros, Inf
|
1589
|
+
>>> from scipy.sparse import csr_matrix
|
1590
|
+
>>> H = csr_matrix(np.array([[1003.1, 4.3, 6.3, 5.9],
|
1591
|
+
... [4.3, 2.2, 2.1, 3.9],
|
1592
|
+
... [6.3, 2.1, 3.5, 4.8],
|
1593
|
+
... [5.9, 3.9, 4.8, 10 ]]))
|
1594
|
+
>>> c = np.zeros(4)
|
1595
|
+
>>> A = csr_matrix(np.array([[1, 1, 1, 1 ],
|
1596
|
+
... [0.17, 0.11, 0.10, 0.18]]))
|
1597
|
+
>>> l = np.array([1, 0.10])
|
1598
|
+
>>> u = np.array([1, Inf])
|
1599
|
+
>>> xmin = np.zeros(4)
|
1600
|
+
>>> xmax = None
|
1601
|
+
>>> x0 = np.array([1, 0, 0, 1])
|
1602
|
+
>>> solution = qps_pips(H, c, A, l, u, xmin, xmax, x0)
|
1603
|
+
>>> round(solution["f"], 11) == 1.09666678128
|
1604
|
+
True
|
1605
|
+
>>> solution["converged"]
|
1606
|
+
True
|
1607
|
+
>>> solution["output"]["iterations"]
|
1608
|
+
10
|
1609
|
+
|
1610
|
+
All parameters are optional except C{H}, C{c}, C{A} and C{l} or C{u}.
|
1611
|
+
@param H: Quadratic cost coefficients.
|
1612
|
+
@type H: csr_matrix
|
1613
|
+
@param c: vector of linear cost coefficients
|
1614
|
+
@type c: array
|
1615
|
+
@param A: Optional linear constraints.
|
1616
|
+
@type A: csr_matrix
|
1617
|
+
@param l: Optional linear constraints. Default values are M{-Inf}.
|
1618
|
+
@type l: array
|
1619
|
+
@param u: Optional linear constraints. Default values are M{Inf}.
|
1620
|
+
@type u: array
|
1621
|
+
@param xmin: Optional lower bounds on the M{x} variables, defaults are
|
1622
|
+
M{-Inf}.
|
1623
|
+
@type xmin: array
|
1624
|
+
@param xmax: Optional upper bounds on the M{x} variables, defaults are
|
1625
|
+
M{Inf}.
|
1626
|
+
@type xmax: array
|
1627
|
+
@param x0: Starting value of optimization vector M{x}.
|
1628
|
+
@type x0: array
|
1629
|
+
@param opt: optional options dictionary with the following keys, all of
|
1630
|
+
which are also optional (default values shown in parentheses)
|
1631
|
+
- C{verbose} (False) - Controls level of progress output
|
1632
|
+
displayed
|
1633
|
+
- C{feastol} (1e-6) - termination tolerance for feasibility
|
1634
|
+
condition
|
1635
|
+
- C{gradtol} (1e-6) - termination tolerance for gradient
|
1636
|
+
condition
|
1637
|
+
- C{comptol} (1e-6) - termination tolerance for
|
1638
|
+
complementarity condition
|
1639
|
+
- C{costtol} (1e-6) - termination tolerance for cost
|
1640
|
+
condition
|
1641
|
+
- C{max_it} (150) - maximum number of iterations
|
1642
|
+
- C{step_control} (False) - set to True to enable step-size
|
1643
|
+
control
|
1644
|
+
- C{max_red} (20) - maximum number of step-size reductions if
|
1645
|
+
step-control is on
|
1646
|
+
- C{cost_mult} (1.0) - cost multiplier used to scale the
|
1647
|
+
objective function for improved conditioning. Note: The
|
1648
|
+
same value must also be passed to the Hessian evaluation
|
1649
|
+
function so that it can appropriately scale the objective
|
1650
|
+
function term in the Hessian of the Lagrangian.
|
1651
|
+
@type opt: dict
|
1652
|
+
|
1653
|
+
@rtype: dict
|
1654
|
+
@return: The solution dictionary has the following keys:
|
1655
|
+
- C{x} - solution vector
|
1656
|
+
- C{f} - final objective function value
|
1657
|
+
- C{converged} - exit status
|
1658
|
+
- True = first order optimality conditions satisfied
|
1659
|
+
- False = maximum number of iterations reached
|
1660
|
+
- None = numerically failed
|
1661
|
+
- C{output} - output dictionary with keys:
|
1662
|
+
- C{iterations} - number of iterations performed
|
1663
|
+
- C{hist} - dictionary of arrays with trajectories of the
|
1664
|
+
following: feascond, gradcond, coppcond, costcond, gamma,
|
1665
|
+
stepsize, obj, alphap, alphad
|
1666
|
+
- C{message} - exit message
|
1667
|
+
- C{lmbda} - dictionary containing the Langrange and Kuhn-Tucker
|
1668
|
+
multipliers on the constraints, with keys:
|
1669
|
+
- C{eqnonlin} - nonlinear equality constraints
|
1670
|
+
- C{ineqnonlin} - nonlinear inequality constraints
|
1671
|
+
- C{mu_l} - lower (left-hand) limit on linear constraints
|
1672
|
+
- C{mu_u} - upper (right-hand) limit on linear constraints
|
1673
|
+
- C{lower} - lower bound on optimization variables
|
1674
|
+
- C{upper} - upper bound on optimization variables
|
1675
|
+
|
1676
|
+
@see: L{pips}
|
1677
|
+
|
1678
|
+
@author: Ray Zimmerman (PSERC Cornell)
|
1679
|
+
"""
|
1680
|
+
if isinstance(H, dict):
|
1681
|
+
p = H
|
1682
|
+
else:
|
1683
|
+
p = {'H': H, 'c': c, 'A': A, 'l': l, 'u': u}
|
1684
|
+
if xmin is not None:
|
1685
|
+
p['xmin'] = xmin
|
1686
|
+
if xmax is not None:
|
1687
|
+
p['xmax'] = xmax
|
1688
|
+
if x0 is not None:
|
1689
|
+
p['x0'] = x0
|
1690
|
+
if opt is not None:
|
1691
|
+
p['opt'] = opt
|
1692
|
+
|
1693
|
+
if 'H' not in p or p['H'] == None: # p['H'].nnz == 0:
|
1694
|
+
if p['A'] is None or p['A'].nnz == 0 and \
|
1695
|
+
'xmin' not in p and \
|
1696
|
+
'xmax' not in p:
|
1697
|
+
# 'xmin' not in p or len(p['xmin']) == 0 and \
|
1698
|
+
# 'xmax' not in p or len(p['xmax']) == 0:
|
1699
|
+
print('qps_pips: LP problem must include constraints or variable bounds')
|
1700
|
+
return
|
1701
|
+
else:
|
1702
|
+
if p['A'] is not None and p['A'].nnz >= 0:
|
1703
|
+
nx = p['A'].shape[1]
|
1704
|
+
elif 'xmin' in p and len(p['xmin']) > 0:
|
1705
|
+
nx = p['xmin'].shape[0]
|
1706
|
+
elif 'xmax' in p and len(p['xmax']) > 0:
|
1707
|
+
nx = p['xmax'].shape[0]
|
1708
|
+
p['H'] = c_sparse((nx, nx))
|
1709
|
+
else:
|
1710
|
+
nx = p['H'].shape[0]
|
1711
|
+
|
1712
|
+
p['xmin'] = -inf * np.ones(nx) if 'xmin' not in p else p['xmin']
|
1713
|
+
p['xmax'] = inf * np.ones(nx) if 'xmax' not in p else p['xmax']
|
1714
|
+
|
1715
|
+
p['c'] = np.zeros(nx) if p['c'] is None else p['c']
|
1716
|
+
|
1717
|
+
p['x0'] = np.zeros(nx) if 'x0' not in p else p['x0']
|
1718
|
+
|
1719
|
+
def qp_f(x, return_hessian=False):
|
1720
|
+
f = 0.5 * np.dot(x * p['H'], x) + np.dot(p['c'], x)
|
1721
|
+
df = p['H'] * x + p['c']
|
1722
|
+
if not return_hessian:
|
1723
|
+
return f, df
|
1724
|
+
d2f = p['H']
|
1725
|
+
return f, df, d2f
|
1726
|
+
|
1727
|
+
p['f_fcn'] = qp_f
|
1728
|
+
|
1729
|
+
sol = pips(p)
|
1730
|
+
|
1731
|
+
return sol["x"], sol["f"], sol["eflag"], sol["output"], sol["lmbda"]
|
1732
|
+
|
1733
|
+
|
1734
|
+
def qps_pypower(H, c=None, A=None, l=None, u=None, xmin=None, xmax=None,
|
1735
|
+
x0=None, opt=None):
|
1736
|
+
"""
|
1737
|
+
Quadratic Program Solver for PYPOWER.
|
1738
|
+
|
1739
|
+
A common wrapper function for various QP solvers.
|
1740
|
+
Solves the following QP (quadratic programming) problem::
|
1741
|
+
|
1742
|
+
min 1/2 x'*H*x + c'*x
|
1743
|
+
x
|
1744
|
+
|
1745
|
+
subject to::
|
1746
|
+
|
1747
|
+
l <= A*x <= u (linear constraints)
|
1748
|
+
xmin <= x <= xmax (variable bounds)
|
1749
|
+
|
1750
|
+
Inputs (all optional except C{H}, C{c}, C{A} and C{l}):
|
1751
|
+
- C{H} : matrix (possibly sparse) of quadratic cost coefficients
|
1752
|
+
- C{c} : vector of linear cost coefficients
|
1753
|
+
- C{A, l, u} : define the optional linear constraints. Default
|
1754
|
+
values for the elements of C{l} and C{u} are -Inf and Inf,
|
1755
|
+
respectively.
|
1756
|
+
- C{xmin}, C{xmax} : optional lower and upper bounds on the
|
1757
|
+
C{x} variables, defaults are -Inf and Inf, respectively.
|
1758
|
+
- C{x0} : optional starting value of optimization vector C{x}
|
1759
|
+
- C{opt} : optional options structure with the following fields,
|
1760
|
+
all of which are also optional (default values shown in parentheses)
|
1761
|
+
- C{alg} (0) - determines which solver to use
|
1762
|
+
- 0 = automatic, first available of BPMPD_MEX, CPLEX,
|
1763
|
+
Gurobi, PIPS
|
1764
|
+
- 100 = BPMPD_MEX
|
1765
|
+
- 200 = PIPS, Python Interior Point Solver
|
1766
|
+
pure Python implementation of a primal-dual
|
1767
|
+
interior point method
|
1768
|
+
- 250 = PIPS-sc, a step controlled variant of PIPS
|
1769
|
+
- 300 = Optimization Toolbox, QUADPROG or LINPROG
|
1770
|
+
- 400 = IPOPT
|
1771
|
+
- 500 = CPLEX
|
1772
|
+
- 600 = MOSEK
|
1773
|
+
- 700 = Gurobi
|
1774
|
+
- C{verbose} (0) - controls level of progress output displayed
|
1775
|
+
- 0 = no progress output
|
1776
|
+
- 1 = some progress output
|
1777
|
+
- 2 = verbose progress output
|
1778
|
+
- C{max_it} (0) - maximum number of iterations allowed
|
1779
|
+
- 0 = use algorithm default
|
1780
|
+
- C{bp_opt} - options vector for BP
|
1781
|
+
- C{cplex_opt} - options dict for CPLEX
|
1782
|
+
- C{grb_opt} - options dict for gurobipy
|
1783
|
+
- C{ipopt_opt} - options dict for IPOPT
|
1784
|
+
- C{pips_opt} - options dict for L{qps_pips}
|
1785
|
+
- C{mosek_opt} - options dict for MOSEK
|
1786
|
+
- C{ot_opt} - options dict for QUADPROG/LINPROG
|
1787
|
+
- C{problem} : The inputs can alternatively be supplied in a single
|
1788
|
+
C{problem} dict with fields corresponding to the input arguments
|
1789
|
+
described above: C{H, c, A, l, u, xmin, xmax, x0, opt}
|
1790
|
+
|
1791
|
+
Outputs:
|
1792
|
+
- C{x} : solution vector
|
1793
|
+
- C{f} : final objective function value
|
1794
|
+
- C{exitflag} : exit flag
|
1795
|
+
- 1 = converged
|
1796
|
+
- 0 or negative values = algorithm specific failure codes
|
1797
|
+
- C{output} : output struct with the following fields:
|
1798
|
+
- C{alg} - algorithm code of solver used
|
1799
|
+
- (others) - algorithm specific fields
|
1800
|
+
- C{lmbda} : dict containing the Langrange and Kuhn-Tucker
|
1801
|
+
multipliers on the constraints, with fields:
|
1802
|
+
- C{mu_l} - lower (left-hand) limit on linear constraints
|
1803
|
+
- C{mu_u} - upper (right-hand) limit on linear constraints
|
1804
|
+
- C{lower} - lower bound on optimization variables
|
1805
|
+
- C{upper} - upper bound on optimization variables
|
1806
|
+
|
1807
|
+
|
1808
|
+
Example from U{http://www.uc.edu/sashtml/iml/chap8/sect12.htm}:
|
1809
|
+
|
1810
|
+
>>> from numpy import array, zeros, Inf
|
1811
|
+
>>> from scipy.sparse import csr_matrix
|
1812
|
+
>>> H = csr_matrix(np.array([[1003.1, 4.3, 6.3, 5.9],
|
1813
|
+
... [4.3, 2.2, 2.1, 3.9],
|
1814
|
+
... [6.3, 2.1, 3.5, 4.8],
|
1815
|
+
... [5.9, 3.9, 4.8, 10 ]]))
|
1816
|
+
>>> c = np.zeros(4)
|
1817
|
+
>>> A = csr_matrix(np.array([[1, 1, 1, 1 ],
|
1818
|
+
... [0.17, 0.11, 0.10, 0.18]]))
|
1819
|
+
>>> l = np.array([1, 0.10])
|
1820
|
+
>>> u = np.array([1, Inf])
|
1821
|
+
>>> xmin = np.zeros(4)
|
1822
|
+
>>> xmax = None
|
1823
|
+
>>> x0 = np.array([1, 0, 0, 1])
|
1824
|
+
>>> solution = qps_pips(H, c, A, l, u, xmin, xmax, x0)
|
1825
|
+
>>> round(solution["f"], 11) == 1.09666678128
|
1826
|
+
True
|
1827
|
+
>>> solution["converged"]
|
1828
|
+
True
|
1829
|
+
>>> solution["output"]["iterations"]
|
1830
|
+
10
|
1831
|
+
|
1832
|
+
@author: Ray Zimmerman (PSERC Cornell)
|
1833
|
+
"""
|
1834
|
+
# ----- input argument handling -----
|
1835
|
+
# gather inputs
|
1836
|
+
if isinstance(H, dict): # problem struct
|
1837
|
+
p = H
|
1838
|
+
if 'opt' in p:
|
1839
|
+
opt = p['opt']
|
1840
|
+
if 'x0' in p:
|
1841
|
+
x0 = p['x0']
|
1842
|
+
if 'xmax' in p:
|
1843
|
+
xmax = p['xmax']
|
1844
|
+
if 'xmin' in p:
|
1845
|
+
xmin = p['xmin']
|
1846
|
+
if 'u' in p:
|
1847
|
+
u = p['u']
|
1848
|
+
if 'l' in p:
|
1849
|
+
l = p['l']
|
1850
|
+
if 'A' in p:
|
1851
|
+
A = p['A']
|
1852
|
+
if 'c' in p:
|
1853
|
+
c = p['c']
|
1854
|
+
if 'H' in p:
|
1855
|
+
H = p['H']
|
1856
|
+
else: # individual args
|
1857
|
+
# assert H is not None zero dimensional sparse matrices not supported
|
1858
|
+
assert c is not None
|
1859
|
+
# assert A is not None zero dimensional sparse matrices not supported
|
1860
|
+
# assert l is not None no lower bounds indicated by None
|
1861
|
+
|
1862
|
+
if opt is None:
|
1863
|
+
opt = {}
|
1864
|
+
# if x0 is None:
|
1865
|
+
# x0 = np.array([])
|
1866
|
+
# if xmax is None:
|
1867
|
+
# xmax = np.array([])
|
1868
|
+
# if xmin is None:
|
1869
|
+
# xmin = np.array([])
|
1870
|
+
|
1871
|
+
# default options
|
1872
|
+
if 'alg' in opt:
|
1873
|
+
alg = opt['alg']
|
1874
|
+
else:
|
1875
|
+
alg = 0
|
1876
|
+
|
1877
|
+
if 'verbose' in opt:
|
1878
|
+
verbose = opt['verbose']
|
1879
|
+
else:
|
1880
|
+
verbose = 0
|
1881
|
+
|
1882
|
+
if alg == 0:
|
1883
|
+
if putils.have_fcn('cplex'): # use CPLEX by default, if available
|
1884
|
+
alg = 500
|
1885
|
+
elif putils.have_fcn('mosek'): # if not, then MOSEK, if available
|
1886
|
+
alg = 600
|
1887
|
+
elif putils.have_fcn('gurobipy'): # if not, then Gurobi, if available
|
1888
|
+
alg = 700
|
1889
|
+
else: # otherwise PIPS
|
1890
|
+
alg = 200
|
1891
|
+
|
1892
|
+
# ----- call the appropriate solver -----
|
1893
|
+
if alg == 200 or alg == 250: # use MIPS or sc-MIPS
|
1894
|
+
# set up options
|
1895
|
+
if 'pips_opt' in opt:
|
1896
|
+
pips_opt = opt['pips_opt']
|
1897
|
+
else:
|
1898
|
+
pips_opt = {}
|
1899
|
+
|
1900
|
+
if 'max_it' in opt:
|
1901
|
+
pips_opt['max_it'] = opt['max_it']
|
1902
|
+
|
1903
|
+
if alg == 200:
|
1904
|
+
pips_opt['step_control'] = False
|
1905
|
+
else:
|
1906
|
+
pips_opt['step_control'] = True
|
1907
|
+
|
1908
|
+
pips_opt['verbose'] = verbose
|
1909
|
+
|
1910
|
+
# call solver
|
1911
|
+
x, f, eflag, output, lmbda = \
|
1912
|
+
qps_pips(H, c, A, l, u, xmin, xmax, x0, pips_opt)
|
1913
|
+
elif alg == 400: # use IPOPT
|
1914
|
+
x, f, eflag, output, lmbda = \
|
1915
|
+
qps_ipopt(H, c, A, l, u, xmin, xmax, x0, opt)
|
1916
|
+
elif alg == 500: # use CPLEX
|
1917
|
+
x, f, eflag, output, lmbda = \
|
1918
|
+
qps_cplex(H, c, A, l, u, xmin, xmax, x0, opt)
|
1919
|
+
elif alg == 600: # use MOSEK
|
1920
|
+
x, f, eflag, output, lmbda = \
|
1921
|
+
qps_mosek(H, c, A, l, u, xmin, xmax, x0, opt)
|
1922
|
+
elif 700: # use Gurobi
|
1923
|
+
x, f, eflag, output, lmbda = \
|
1924
|
+
qps_gurobi(H, c, A, l, u, xmin, xmax, x0, opt)
|
1925
|
+
else:
|
1926
|
+
logger.info('qps_pypower: %d is not a valid algorithm code', alg)
|
1927
|
+
|
1928
|
+
if 'alg' not in output:
|
1929
|
+
output['alg'] = alg
|
1930
|
+
|
1931
|
+
return x, f, eflag, output, lmbda
|
1932
|
+
|
1933
|
+
|
1934
|
+
def cplex_options(overrides=None, ppopt=None):
|
1935
|
+
"""
|
1936
|
+
Sets options for CPLEX.
|
1937
|
+
|
1938
|
+
Sets the values for the options dict normally passed to
|
1939
|
+
C{cplexoptimset}.
|
1940
|
+
|
1941
|
+
Inputs are all optional, second argument must be either a string
|
1942
|
+
(C{fname}) or a dict (C{ppopt}):
|
1943
|
+
|
1944
|
+
Output is an options dict to pass to C{cplexoptimset}.
|
1945
|
+
|
1946
|
+
Example:
|
1947
|
+
|
1948
|
+
If C{ppopt['CPLEX_OPT'] = 3}, then after setting the default CPLEX options,
|
1949
|
+
CPLEX_OPTIONS will execute the following user-defined function
|
1950
|
+
to allow option overrides::
|
1951
|
+
|
1952
|
+
opt = cplex_user_options_3(opt, ppopt)
|
1953
|
+
|
1954
|
+
The contents of cplex_user_options_3.py, could be something like::
|
1955
|
+
|
1956
|
+
def cplex_user_options_3(opt, ppopt):
|
1957
|
+
opt = {}
|
1958
|
+
opt['threads'] = 2
|
1959
|
+
opt['simplex']['refactor'] = 1
|
1960
|
+
opt['timelimit'] = 10000
|
1961
|
+
return opt
|
1962
|
+
|
1963
|
+
For details on the available options, see the I{"Parameters Reference
|
1964
|
+
Manual"} section of the CPLEX documentation at:
|
1965
|
+
U{http://publib.boulder.ibm.com/infocenter/cosinfoc/v12r2/}
|
1966
|
+
|
1967
|
+
@param overrides:
|
1968
|
+
- dict containing values to override the defaults
|
1969
|
+
- fname: name of user-supplied function called after default
|
1970
|
+
options are set to modify them. Calling syntax is::
|
1971
|
+
|
1972
|
+
modified_opt = fname(default_opt)
|
1973
|
+
|
1974
|
+
@param ppopt: PYPOWER options vector, uses the following entries:
|
1975
|
+
- OPF_VIOLATION - used to set opt.simplex.tolerances.feasibility
|
1976
|
+
- VERBOSE - used to set opt.barrier.display,
|
1977
|
+
opt.conflict.display, opt.mip.display, opt.sifting.display,
|
1978
|
+
opt.simplex.display, opt.tune.display
|
1979
|
+
- CPLEX_LPMETHOD - used to set opt.lpmethod
|
1980
|
+
- CPLEX_QPMETHOD - used to set opt.qpmethod
|
1981
|
+
- CPLEX_OPT - user option file, if ppopt['CPLEX_OPT'] is
|
1982
|
+
non-zero it is appended to 'cplex_user_options_' to form
|
1983
|
+
the name of a user-supplied function used as C{fname}
|
1984
|
+
described above, except with calling syntax::
|
1985
|
+
|
1986
|
+
modified_opt = fname(default_opt, ppopt)
|
1987
|
+
|
1988
|
+
@see: C{cplexlp}, C{cplexqp}, L{ppoption}.
|
1989
|
+
|
1990
|
+
@author: Ray Zimmerman (PSERC Cornell)
|
1991
|
+
"""
|
1992
|
+
# ----- initialization and arg handling -----
|
1993
|
+
# defaults
|
1994
|
+
verbose = 1
|
1995
|
+
feastol = 1e-6
|
1996
|
+
fname = ''
|
1997
|
+
|
1998
|
+
# second argument
|
1999
|
+
if ppopt != None:
|
2000
|
+
if isinstance(ppopt, str): # 2nd arg is FNAME (string)
|
2001
|
+
fname = ppopt
|
2002
|
+
have_ppopt = False
|
2003
|
+
else: # 2nd arg is ppopt (MATPOWER options vector)
|
2004
|
+
have_ppopt = True
|
2005
|
+
# (make default OPF_VIOLATION correspond to default CPLEX feastol)
|
2006
|
+
feastol = ppopt['OPF_VIOLATION'] / 5
|
2007
|
+
verbose = ppopt['VERBOSE']
|
2008
|
+
lpmethod = ppopt['CPLEX_LPMETHOD']
|
2009
|
+
qpmethod = ppopt['CPLEX_QPMETHOD']
|
2010
|
+
if ppopt['CPLEX_OPT']:
|
2011
|
+
fname = 'cplex_user_options_#d' % ppopt['CPLEX_OPT']
|
2012
|
+
else:
|
2013
|
+
have_ppopt = False
|
2014
|
+
|
2015
|
+
# ----- set default options for CPLEX -----
|
2016
|
+
opt = cplexoptimset('cplex')
|
2017
|
+
opt['simplex']['tolerances']['feasibility'] = feastol
|
2018
|
+
|
2019
|
+
# printing
|
2020
|
+
vrb = max([0, verbose - 1])
|
2021
|
+
opt['barrier']['display'] = vrb
|
2022
|
+
opt['conflict']['display'] = vrb
|
2023
|
+
opt['mip']['display'] = vrb
|
2024
|
+
opt['sifting']['display'] = vrb
|
2025
|
+
opt['simplex']['display'] = vrb
|
2026
|
+
opt['tune']['display'] = vrb
|
2027
|
+
|
2028
|
+
# solution algorithm
|
2029
|
+
if have_ppopt:
|
2030
|
+
opt['lpmethod'] = lpmethod
|
2031
|
+
opt['qpmethod'] = qpmethod
|
2032
|
+
# else:
|
2033
|
+
# opt['lpmethod'] = 2
|
2034
|
+
# opt['qpmethod'] = 2
|
2035
|
+
|
2036
|
+
# ----- call user function to modify defaults -----
|
2037
|
+
if len(fname) > 0:
|
2038
|
+
if have_ppopt:
|
2039
|
+
opt = putils.feval(fname, opt, ppopt)
|
2040
|
+
else:
|
2041
|
+
opt = putils.feval(fname, opt)
|
2042
|
+
|
2043
|
+
# ----- apply overrides -----
|
2044
|
+
if overrides is not None:
|
2045
|
+
names = overrides.keys()
|
2046
|
+
for k in range(len(names)):
|
2047
|
+
if isinstance(overrides[names[k]], dict):
|
2048
|
+
names2 = overrides[names[k]].keys()
|
2049
|
+
for k2 in range(len(names2)):
|
2050
|
+
if isinstance(overrides[names[k]][names2[k2]], dict):
|
2051
|
+
names3 = overrides[names[k]][names2[k2]].keys()
|
2052
|
+
for k3 in range(len(names3)):
|
2053
|
+
opt[names[k]][names2[k2]][names3[k3]] = overrides[names[k]][names2[k2]][names3[k3]]
|
2054
|
+
else:
|
2055
|
+
opt[names[k]][names2[k2]] = overrides[names[k]][names2[k2]]
|
2056
|
+
else:
|
2057
|
+
opt[names[k]] = overrides[names[k]]
|
2058
|
+
|
2059
|
+
return opt
|
2060
|
+
|
2061
|
+
|
2062
|
+
def gurobi_options(overrides=None, ppopt=None):
|
2063
|
+
"""
|
2064
|
+
Sets options for GUROBI.
|
2065
|
+
|
2066
|
+
Sets the values for the options dict normally passed to GUROBI_MEX.
|
2067
|
+
|
2068
|
+
Inputs are all optional, second argument must be either a string
|
2069
|
+
(fname) or a vector (ppopt):
|
2070
|
+
|
2071
|
+
overrides - dict containing values to override the defaults
|
2072
|
+
fname - name of user-supplied function called after default
|
2073
|
+
options are set to modify them. Calling syntax is:
|
2074
|
+
modified_opt = fname(default_opt)
|
2075
|
+
ppopt - PYPOWER options vector, uses the following entries:
|
2076
|
+
OPF_VIOLATION (16) - used to set opt.FeasibilityTol
|
2077
|
+
VERBOSE (31) - used to set opt.DisplayInterval, opt.Display
|
2078
|
+
GRB_METHOD (121) - used to set opt.Method
|
2079
|
+
GRB_TIMELIMIT (122) - used to set opt.TimeLimit (seconds)
|
2080
|
+
GRB_THREADS (123) - used to set opt.Threads
|
2081
|
+
GRB_OPT (124) - user option file, if PPOPT(124) is non-zero
|
2082
|
+
it is appended to 'gurobi_user_options_' to form the name of a
|
2083
|
+
user-supplied function used as C{fname} described above, except
|
2084
|
+
with calling syntax:
|
2085
|
+
modified_opt = fname(default_opt, mpopt)
|
2086
|
+
|
2087
|
+
Output is an options struct to pass to GUROBI_MEX.
|
2088
|
+
|
2089
|
+
Example:
|
2090
|
+
|
2091
|
+
If ppopt['GRB_OPT'] = 3, then after setting the default GUROBI options,
|
2092
|
+
GUROBI_OPTIONS will execute the following user-defined function
|
2093
|
+
to allow option overrides:
|
2094
|
+
|
2095
|
+
opt = gurobi_user_options_3(opt, ppopt)
|
2096
|
+
|
2097
|
+
The contents of gurobi_user_options_3.py, could be something like:
|
2098
|
+
|
2099
|
+
def gurobi_user_options_3(opt, ppopt):
|
2100
|
+
opt = {}
|
2101
|
+
opt['OptimalityTol'] = 1e-9
|
2102
|
+
opt['IterationLimit'] = 3000
|
2103
|
+
opt['BarIterLimit'] = 200
|
2104
|
+
opt['Crossover'] = 0
|
2105
|
+
opt['Presolve'] = 0
|
2106
|
+
return opt
|
2107
|
+
|
2108
|
+
For details on the available options, see the "Parameters" section
|
2109
|
+
of the "Gurobi Optimizer Reference Manual" at:
|
2110
|
+
|
2111
|
+
http://www.gurobi.com/doc/45/refman/
|
2112
|
+
|
2113
|
+
@see: L{gurobi_mex}, L{ppoption}.
|
2114
|
+
"""
|
2115
|
+
# ----- initialization and arg handling -----
|
2116
|
+
# defaults
|
2117
|
+
verbose = True
|
2118
|
+
fname = ''
|
2119
|
+
|
2120
|
+
# second argument
|
2121
|
+
if ppopt != None:
|
2122
|
+
if isinstance(ppopt, str): # 2nd arg is FNAME (string)
|
2123
|
+
fname = ppopt
|
2124
|
+
have_ppopt = False
|
2125
|
+
else: # 2nd arg is MPOPT (MATPOWER options vector)
|
2126
|
+
have_ppopt = True
|
2127
|
+
verbose = ppopt['VERBOSE']
|
2128
|
+
if ppopt['GRB_OPT']:
|
2129
|
+
fname = 'gurobi_user_options_%d', ppopt['GRB_OPT']
|
2130
|
+
else:
|
2131
|
+
have_ppopt = False
|
2132
|
+
|
2133
|
+
# ----- set default options for CPLEX -----
|
2134
|
+
opt = {}
|
2135
|
+
# opt['OptimalityTol'] = 1e-6
|
2136
|
+
# -1 - auto, 0 - no, 1 - conserv, 2 - aggressive=
|
2137
|
+
# opt['Presolve'] = -1
|
2138
|
+
# opt['LogFile'] = 'qps_gurobi.log'
|
2139
|
+
if have_ppopt:
|
2140
|
+
# (make default OPF_VIOLATION correspond to default FeasibilityTol)
|
2141
|
+
opt['FeasibilityTol'] = ppopt['OPF_VIOLATION'] / 5
|
2142
|
+
opt['Method'] = ppopt['GRB_METHOD']
|
2143
|
+
opt['TimeLimit'] = ppopt['GRB_TIMELIMIT']
|
2144
|
+
opt['Threads'] = ppopt['GRB_THREADS']
|
2145
|
+
else:
|
2146
|
+
opt['Method'] = 1 # dual simplex
|
2147
|
+
|
2148
|
+
opt['Display'] = min(verbose, 3)
|
2149
|
+
if verbose:
|
2150
|
+
opt['DisplayInterval'] = 1
|
2151
|
+
else:
|
2152
|
+
opt['DisplayInterval'] = inf
|
2153
|
+
|
2154
|
+
# ----- call user function to modify defaults -----
|
2155
|
+
if len(fname) > 0:
|
2156
|
+
if have_ppopt:
|
2157
|
+
opt = putils.feval(fname, opt, ppopt)
|
2158
|
+
else:
|
2159
|
+
opt = putils.feval(fname, opt)
|
2160
|
+
|
2161
|
+
# ----- apply overrides -----
|
2162
|
+
if overrides is not None:
|
2163
|
+
names = overrides.keys()
|
2164
|
+
for k in range(len(names)):
|
2165
|
+
opt[names[k]] = overrides[names[k]]
|
2166
|
+
|
2167
|
+
return opt
|
2168
|
+
|
2169
|
+
|
2170
|
+
def ipopt_options(overrides=None, ppopt=None):
|
2171
|
+
"""
|
2172
|
+
Sets options for IPOPT.
|
2173
|
+
|
2174
|
+
Sets the values for the options.ipopt dict normally passed to
|
2175
|
+
IPOPT.
|
2176
|
+
|
2177
|
+
Inputs are all optional, second argument must be either a string
|
2178
|
+
(C{fname}) or a dict (C{ppopt}):
|
2179
|
+
|
2180
|
+
- C{overrides}
|
2181
|
+
- dict containing values to override the defaults
|
2182
|
+
- C{fname} name of user-supplied function called after default
|
2183
|
+
options are set to modify them. Calling syntax is::
|
2184
|
+
modified_opt = fname(default_opt)
|
2185
|
+
- C{ppopt} PYPOWER options vector, uses the following entries:
|
2186
|
+
- C{OPF_VIOLATION} used to set opt['constr_viol_tol']
|
2187
|
+
- C{VERBOSE} used to opt['print_level']
|
2188
|
+
- C{IPOPT_OPT} user option file, if ppopt['IPOPT_OPT'] is
|
2189
|
+
non-zero it is appended to 'ipopt_user_options_' to form
|
2190
|
+
the name of a user-supplied function used as C{fname}
|
2191
|
+
described above, except with calling syntax::
|
2192
|
+
modified_opt = fname(default_opt ppopt)
|
2193
|
+
|
2194
|
+
Output is an options.ipopt dict to pass to IPOPT.
|
2195
|
+
|
2196
|
+
Example: If ppopt['IPOPT_OPT'] = 3, then after setting the default IPOPT
|
2197
|
+
options, L{ipopt_options} will execute the following user-defined function
|
2198
|
+
to allow option overrides::
|
2199
|
+
|
2200
|
+
opt = ipopt_user_options_3(opt, ppopt);
|
2201
|
+
|
2202
|
+
The contents of ipopt_user_options_3.py, could be something like::
|
2203
|
+
|
2204
|
+
def ipopt_user_options_3(opt, ppopt):
|
2205
|
+
opt = {}
|
2206
|
+
opt['nlp_scaling_method'] = 'none'
|
2207
|
+
opt['max_iter'] = 500
|
2208
|
+
opt['derivative_test'] = 'first-order'
|
2209
|
+
return opt
|
2210
|
+
|
2211
|
+
See the options reference section in the IPOPT documentation for
|
2212
|
+
details on the available options.
|
2213
|
+
|
2214
|
+
U{http://www.coin-or.org/Ipopt/documentation/}
|
2215
|
+
|
2216
|
+
@see: C{pyipopt}, L{ppoption}
|
2217
|
+
|
2218
|
+
@author: Ray Zimmerman (PSERC Cornell)
|
2219
|
+
"""
|
2220
|
+
# ----- initialization and arg handling -----
|
2221
|
+
# defaults
|
2222
|
+
verbose = 2
|
2223
|
+
fname = ''
|
2224
|
+
|
2225
|
+
# second argument
|
2226
|
+
if ppopt != None:
|
2227
|
+
if isinstance(ppopt, str): # 2nd arg is FNAME (string)
|
2228
|
+
fname = ppopt
|
2229
|
+
have_ppopt = False
|
2230
|
+
else: # 2nd arg is ppopt (MATPOWER options vector)
|
2231
|
+
have_ppopt = True
|
2232
|
+
verbose = ppopt['VERBOSE']
|
2233
|
+
if ppopt['IPOPT_OPT']:
|
2234
|
+
fname = 'ipopt_user_options_#d' % ppopt['IPOPT_OPT']
|
2235
|
+
else:
|
2236
|
+
have_ppopt = False
|
2237
|
+
|
2238
|
+
opt = {}
|
2239
|
+
# ----- set default options for IPOPT -----
|
2240
|
+
# printing
|
2241
|
+
if verbose:
|
2242
|
+
opt['print_level'] = min([12, verbose * 2 + 1])
|
2243
|
+
else:
|
2244
|
+
opt['print_level'] = 0
|
2245
|
+
|
2246
|
+
# convergence
|
2247
|
+
opt['tol'] = 1e-8 # default 1e-8
|
2248
|
+
opt['max_iter'] = 250 # default 3000
|
2249
|
+
opt['dual_inf_tol'] = 0.1 # default 1
|
2250
|
+
if have_ppopt:
|
2251
|
+
opt['constr_viol_tol'] = ppopt[16] # default 1e-4
|
2252
|
+
opt['acceptable_constr_viol_tol'] = ppopt[16] * 100 # default 1e-2
|
2253
|
+
opt['compl_inf_tol'] = 1e-5 # default 1e-4
|
2254
|
+
opt['acceptable_tol'] = 1e-8 # default 1e-6
|
2255
|
+
# opt['acceptable_iter'] = 15 ## default 15
|
2256
|
+
# opt['acceptable_dual_inf_tol'] = 1e+10 ## default 1e+10
|
2257
|
+
opt['acceptable_compl_inf_tol'] = 1e-3 # default 1e-2
|
2258
|
+
# opt['acceptable_obj_change_tol'] = 1e+20 ## default 1e+20
|
2259
|
+
# opt['diverging_iterates_tol'] = 1e+20 ## default 1e+20
|
2260
|
+
|
2261
|
+
# NLP scaling
|
2262
|
+
# opt['nlp_scaling_method'] = 'none' ## default 'gradient-based'
|
2263
|
+
|
2264
|
+
# NLP
|
2265
|
+
# opt['fixed_variable_treatment'] = 'make_constraint' ## default 'make_parameter'
|
2266
|
+
# opt['honor_original_bounds'] = 'no' ## default 'yes'
|
2267
|
+
# opt['check_derivatives_for_naninf'] = 'yes' ## default 'no'
|
2268
|
+
|
2269
|
+
# initialization
|
2270
|
+
# opt['least_square_init_primal'] = 'yes' ## default 'no'
|
2271
|
+
# opt['least_square_init_duals'] = 'yes' ## default 'no'
|
2272
|
+
|
2273
|
+
# barrier parameter update
|
2274
|
+
opt['mu_strategy'] = 'adaptive' # default 'monotone'
|
2275
|
+
|
2276
|
+
# linear solver
|
2277
|
+
# opt['linear_solver'] = 'ma27'
|
2278
|
+
# opt['linear_solver'] = 'ma57'
|
2279
|
+
# opt['linear_solver'] = 'pardiso'
|
2280
|
+
# opt['linear_solver'] = 'wsmp'
|
2281
|
+
# opt['linear_solver'] = 'mumps' ## default 'mumps'
|
2282
|
+
# opt['linear_solver'] = 'custom'
|
2283
|
+
# opt['linear_scaling_on_demand'] = 'no' ## default 'yes'
|
2284
|
+
|
2285
|
+
# step calculation
|
2286
|
+
# opt['mehrotra_algorithm'] = 'yes' ## default 'no'
|
2287
|
+
# opt['fast_step_computation'] = 'yes' ## default 'no'
|
2288
|
+
|
2289
|
+
# restoration phase
|
2290
|
+
# opt['expect_infeasible_problem'] = 'yes' ## default 'no'
|
2291
|
+
|
2292
|
+
# derivative checker
|
2293
|
+
# opt['derivative_test'] = 'second-order' ## default 'none'
|
2294
|
+
|
2295
|
+
# hessian approximation
|
2296
|
+
# opt['hessian_approximation'] = 'limited-memory' ## default 'exact'
|
2297
|
+
|
2298
|
+
# ma57 options
|
2299
|
+
# opt['ma57_pre_alloc'] = 3
|
2300
|
+
# opt['ma57_pivot_order'] = 4
|
2301
|
+
|
2302
|
+
# ----- call user function to modify defaults -----
|
2303
|
+
if len(fname) > 0:
|
2304
|
+
if have_ppopt:
|
2305
|
+
opt = putils.feval(fname, opt, ppopt)
|
2306
|
+
else:
|
2307
|
+
opt = putils.feval(fname, opt)
|
2308
|
+
|
2309
|
+
# ----- apply overrides -----
|
2310
|
+
if overrides is not None:
|
2311
|
+
names = overrides.keys()
|
2312
|
+
for k in range(len(names)):
|
2313
|
+
opt[names[k]] = overrides[names[k]]
|
2314
|
+
|
2315
|
+
return opt
|
2316
|
+
|
2317
|
+
|
2318
|
+
def mosek_options(overrides=None, ppopt=None):
|
2319
|
+
"""Sets options for MOSEK.
|
2320
|
+
|
2321
|
+
Inputs are all optional, second argument must be either a string
|
2322
|
+
(C{fname}) or a dict (C{ppopt}):
|
2323
|
+
|
2324
|
+
- C{overrides}
|
2325
|
+
- dict containing values to override the defaults
|
2326
|
+
- C{fname} name of user-supplied function called after default
|
2327
|
+
options are set to modify them. Calling syntax is::
|
2328
|
+
modified_opt = fname(default_opt)
|
2329
|
+
- C{ppopt} PYPOWER options vector, uses the following entries:
|
2330
|
+
- C{OPF_VIOLATION} used to set opt.MSK_DPAR_INTPNT_TOL_PFEAS
|
2331
|
+
- C{VERBOSE} not currently used here
|
2332
|
+
- C{MOSEK_LP_ALG} - used to set opt.MSK_IPAR_OPTIMIZER
|
2333
|
+
- C{MOSEK_MAX_IT} used to set opt.MSK_IPAR_INTPNT_MAX_ITERATIONS
|
2334
|
+
- C{MOSEK_GAP_TOL} used to set opt.MSK_DPAR_INTPNT_TOL_REL_GAP
|
2335
|
+
- C{MOSEK_MAX_TIME} used to set opt.MSK_DPAR_OPTIMIZER_MAX_TIME
|
2336
|
+
- C{MOSEK_NUM_THREADS} used to set opt.MSK_IPAR_INTPNT_NUM_THREADS
|
2337
|
+
- C{MOSEK_OPT} user option file, if ppopt['MOSEK_OPT'] is non-zero
|
2338
|
+
it is appended to 'mosek_user_options_' to form
|
2339
|
+
the name of a user-supplied function used as C{fname}
|
2340
|
+
described above, except with calling syntax::
|
2341
|
+
modified_opt = fname(default_opt, ppopt)
|
2342
|
+
|
2343
|
+
Output is a param dict to pass to MOSEKOPT.
|
2344
|
+
|
2345
|
+
Example:
|
2346
|
+
|
2347
|
+
If PPOPT['MOSEK_OPT'] = 3, then after setting the default MOSEK options,
|
2348
|
+
L{mosek_options} will execute the following user-defined function
|
2349
|
+
to allow option overrides::
|
2350
|
+
|
2351
|
+
opt = mosek_user_options_3(opt, ppopt)
|
2352
|
+
|
2353
|
+
The contents of mosek_user_options_3.py, could be something like::
|
2354
|
+
|
2355
|
+
def mosek_user_options_3(opt, ppopt):
|
2356
|
+
opt = {}
|
2357
|
+
opt.MSK_DPAR_INTPNT_TOL_DFEAS = 1e-9
|
2358
|
+
opt.MSK_IPAR_SIM_MAX_ITERATIONS = 5000000
|
2359
|
+
return opt
|
2360
|
+
|
2361
|
+
See the Parameters reference in Appix E of "The MOSEK
|
2362
|
+
optimization toolbox for MATLAB manaul" for
|
2363
|
+
details on the available options.
|
2364
|
+
|
2365
|
+
U{http://www.mosek.com/documentation/}
|
2366
|
+
|
2367
|
+
@see: C{mosekopt}, L{ppoption}.
|
2368
|
+
|
2369
|
+
@author: Ray Zimmerman (PSERC Cornell)
|
2370
|
+
"""
|
2371
|
+
# ----- initialization and arg handling -----
|
2372
|
+
# defaults
|
2373
|
+
verbose = 2
|
2374
|
+
gaptol = 0
|
2375
|
+
fname = ''
|
2376
|
+
|
2377
|
+
# get symbolic constant names
|
2378
|
+
r, res = mosekopt('symbcon echo(0)')
|
2379
|
+
sc = res['symbcon']
|
2380
|
+
|
2381
|
+
# second argument
|
2382
|
+
if ppopt == None:
|
2383
|
+
if isinstance(ppopt, str): # 2nd arg is FNAME (string)
|
2384
|
+
fname = ppopt
|
2385
|
+
have_ppopt = False
|
2386
|
+
else: # 2nd arg is ppopt (MATPOWER options vector)
|
2387
|
+
have_ppopt = True
|
2388
|
+
verbose = ppopt['VERBOSE']
|
2389
|
+
if ppopt['MOSEK_OPT']:
|
2390
|
+
fname = 'mosek_user_options_#d' # ppopt['MOSEK_OPT']
|
2391
|
+
else:
|
2392
|
+
have_ppopt = False
|
2393
|
+
|
2394
|
+
opt = {}
|
2395
|
+
# ----- set default options for MOSEK -----
|
2396
|
+
# solution algorithm
|
2397
|
+
if have_ppopt:
|
2398
|
+
alg = ppopt['MOSEK_LP_ALG']
|
2399
|
+
if alg == sc['MSK_OPTIMIZER_FREE'] or \
|
2400
|
+
alg == sc['MSK_OPTIMIZER_INTPNT'] or \
|
2401
|
+
alg == sc['MSK_OPTIMIZER_PRIMAL_SIMPLEX'] or \
|
2402
|
+
alg == sc['MSK_OPTIMIZER_DUAL_SIMPLEX'] or \
|
2403
|
+
alg == sc['MSK_OPTIMIZER_PRIMAL_DUAL_SIMPLEX'] or \
|
2404
|
+
alg == sc['MSK_OPTIMIZER_FREE_SIMPLEX'] or \
|
2405
|
+
alg == sc['MSK_OPTIMIZER_CONCURRENT']:
|
2406
|
+
opt['MSK_IPAR_OPTIMIZER'] = alg
|
2407
|
+
else:
|
2408
|
+
opt['MSK_IPAR_OPTIMIZER'] = sc['MSK_OPTIMIZER_FREE']
|
2409
|
+
|
2410
|
+
# (make default OPF_VIOLATION correspond to default MSK_DPAR_INTPNT_TOL_PFEAS)
|
2411
|
+
opt['MSK_DPAR_INTPNT_TOL_PFEAS'] = ppopt['OPF_VIOLATION'] / 500
|
2412
|
+
if ppopt['MOSEK_MAX_IT']:
|
2413
|
+
opt['MSK_IPAR_INTPNT_MAX_ITERATIONS'] = ppopt['MOSEK_MAX_IT']
|
2414
|
+
|
2415
|
+
if ppopt['MOSEK_GAP_TOL']:
|
2416
|
+
opt['MSK_DPAR_INTPNT_TOL_REL_GAP'] = ppopt['MOSEK_GAP_TOL']
|
2417
|
+
|
2418
|
+
if ppopt['MOSEK_MAX_TIME']:
|
2419
|
+
opt['MSK_DPAR_OPTIMIZER_MAX_TIME'] = ppopt['MOSEK_MAX_TIME']
|
2420
|
+
|
2421
|
+
if ppopt['MOSEK_NUM_THREADS']:
|
2422
|
+
opt['MSK_IPAR_INTPNT_NUM_THREADS'] = ppopt['MOSEK_NUM_THREADS']
|
2423
|
+
else:
|
2424
|
+
opt['MSK_IPAR_OPTIMIZER'] = sc['MSK_OPTIMIZER_FREE']
|
2425
|
+
|
2426
|
+
# opt['MSK_DPAR_INTPNT_TOL_PFEAS'] = 1e-8 ## primal feasibility tol
|
2427
|
+
# opt['MSK_DPAR_INTPNT_TOL_DFEAS'] = 1e-8 ## dual feasibility tol
|
2428
|
+
# opt['MSK_DPAR_INTPNT_TOL_MU_RED'] = 1e-16 ## relative complementarity gap tol
|
2429
|
+
# opt['MSK_DPAR_INTPNT_TOL_REL_GAP'] = 1e-8 ## relative gap termination tol
|
2430
|
+
# opt['MSK_IPAR_INTPNT_MAX_ITERATIONS'] = 400 ## max iterations for int point
|
2431
|
+
# opt['MSK_IPAR_SIM_MAX_ITERATIONS'] = 10000000 ## max iterations for simplex
|
2432
|
+
# opt['MSK_DPAR_OPTIMIZER_MAX_TIME'] = -1 ## max time allowed (< 0 --> Inf)
|
2433
|
+
# opt['MSK_IPAR_INTPNT_NUM_THREADS'] = 1 ## number of threads
|
2434
|
+
# opt['MSK_IPAR_PRESOLVE_USE'] = sc['MSK_PRESOLVE_MODE_OFF']
|
2435
|
+
|
2436
|
+
# if verbose == 0:
|
2437
|
+
# opt['MSK_IPAR_LOG'] = 0
|
2438
|
+
#
|
2439
|
+
|
2440
|
+
# ----- call user function to modify defaults -----
|
2441
|
+
if len(fname) > 0:
|
2442
|
+
if have_ppopt:
|
2443
|
+
opt = putils.feval(fname, opt, ppopt)
|
2444
|
+
else:
|
2445
|
+
opt = putils.feval(fname, opt)
|
2446
|
+
|
2447
|
+
# ----- apply overrides -----
|
2448
|
+
if overrides is not None:
|
2449
|
+
names = overrides.keys()
|
2450
|
+
for k in range(len(names)):
|
2451
|
+
opt[names[k]] = overrides[names[k]]
|