CONOPT
Loading...
Searching...
No Matches
pinadd2err.py
Go to the documentation of this file.
7
8import os
9import sys
10import math
11
12import conopt as co
13
14sys.path.append('../common/')
15import std
16
17
18class PinAdd2ErrModelData(co.ModelData):
19 """ """
20
21 def __init__(self):
22 self.CONS_SEQ = 0
23 self.CONS_DREV = 1
24
25 self.T = 0
26
27 # index arrays for the variables
28 self.vartd = []
29 self.varcs = []
30 self.vars = []
31 self.vard = []
32 self.varr = []
33 self.varp = []
34 self.varrev = []
35
36 # index arrays for the constraints
37 self.consttdeq = []
38 self.constseq = []
39 self.constcseq = []
40 self.constdeq = []
41 self.constreq = []
42 self.constdrev = []
43
44 self.consmapping = {}
45
46 self.hessianstart = []
47 super().__init__()
48
49 def buildModel(self, T, xkeep, xstat, estat):
50 """
51 adding the variables and constraints to the model
52 @ingroup PYTHON1THREAD_PINADD2ERR
53 """
54
55 self.T = T
56 demand = [1.0 + 2.3 * pow(1.015, t) for t in range(T)]
57
58 # adding the variables to the model
59 for t in range(T):
60 # td: Lower=0, Upper=inf, Curr=18
61 varidx = self.addVariable(0, co.Conopt.Infinity, 18)
62 self.vartd.append(varidx)
63
64 # cs: Lower=0, Upper=inf, Curr=7*t
65 varidx = self.addVariable(0, co.Conopt.Infinity, 7 * (t + 1))
66 self.varcs.append(varidx)
67
68 # s: Lower=0, Upper=inf, Curr=7
69 varidx = self.addVariable(0, co.Conopt.Infinity, 7)
70 self.vars.append(varidx)
71
72 # d: Lower=0, Upper=inf, Curr=td-s
73 varidx = self.addVariable(
74 0,
75 co.Conopt.Infinity,
76 self.getVariable(self.vartd[t]).curr
77 - self.getVariable(self.vars[t]).curr,
78 )
79 self.vard.append(varidx)
80
81 # r: Lower=1, Upper=inf, Curr=r(t-1)-d
82 if t > 0:
83 varidx = self.addVariable(
84 1,
85 co.Conopt.Infinity,
86 self.getVariable(self.varr[t - 1]).curr
87 - self.getVariable(self.vard[t]).curr,
88 )
89 else:
90 varidx = self.addVariable(
91 1,
92 co.Conopt.Infinity,
93 500 - self.getVariable(self.vard[t]).curr,
94 )
95 self.varr.append(varidx)
96
97 # p: Lower=1, Upper=inf, Curr=14
98 varidx = self.addVariable(1, co.Conopt.Infinity, 14)
99 self.varp.append(varidx)
100
101 # rev: Lower=-inf, Upper=inf, Curr=0
102 varidx = self.addVariable(-co.Conopt.Infinity, co.Conopt.Infinity)
103 self.varrev.append(varidx)
104
105 # This is a restart: Use the initial values from last solve for
106 # the variables in the first periods and extrapolate the last
107 # period using a linear extrapolation between the last two
108 # periods.
109 if len(xkeep) > 0:
110 for i in range(len(xkeep)):
111 self.getVariable(i).curr = xkeep[i]
112
113 # linear extrapolation for the last period of variable td
114 self.getVariable(self.vartd[T - 1]).curr = (
115 2 * self.getVariable(self.vartd[T - 2]).curr
116 - self.getVariable(self.vartd[T - 3]).curr
117 )
118 # cs
119 self.getVariable(self.varcs[T - 1]).curr = (
120 2 * self.getVariable(self.varcs[T - 2]).curr
121 - self.getVariable(self.varcs[T - 3]).curr
122 )
123 # s
124 self.getVariable(self.vars[T - 1]).curr = (
125 2 * self.getVariable(self.vars[T - 2]).curr
126 - self.getVariable(self.vars[T - 3]).curr
127 )
128 # d
129 self.getVariable(self.vard[T - 1]).curr = (
130 2 * self.getVariable(self.vard[T - 2]).curr
131 - self.getVariable(self.vard[T - 3]).curr
132 )
133 # r
134 self.getVariable(self.varr[T - 1]).curr = (
135 2 * self.getVariable(self.varr[T - 2]).curr
136 - self.getVariable(self.varr[T - 3]).curr
137 )
138 # p
139 self.getVariable(self.varp[T - 1]).curr = (
140 2 * self.getVariable(self.varp[T - 2]).curr
141 - self.getVariable(self.varp[T - 3]).curr
142 )
143 # rev
144 self.getVariable(self.varrev[T - 1]).curr = (
145 2 * self.getVariable(self.varrev[T - 2]).curr
146 - self.getVariable(self.varrev[T - 3]).curr
147 )
148
149 # The variables from the last solve are given the old status and
150 # the variables in the new period are given those in the last
151 # period. Similarly with the Equation status:
152 for i in range(len(xstat)):
153 self.getVariable(i).varstatus = xstat[i]
154
155 # td
156 self.getVariable(self.vartd[T - 1]).varstatus = self.getVariable(
157 self.vartd[T - 2]
158 ).varstatus
159 # cs
160 self.getVariable(self.varcs[T - 1]).varstatus = self.getVariable(
161 self.varcs[T - 2]
162 ).varstatus
163 # s
164 self.getVariable(self.vars[T - 1]).varstatus = self.getVariable(
165 self.vars[T - 2]
166 ).varstatus
167 # d
168 self.getVariable(self.vard[T - 1]).varstatus = self.getVariable(
169 self.vard[T - 2]
170 ).varstatus
171 # r
172 self.getVariable(self.varr[T - 1]).varstatus = self.getVariable(
173 self.varr[T - 2]
174 ).varstatus
175 # p
176 self.getVariable(self.varp[T - 1]).varstatus = self.getVariable(
177 self.varp[T - 2]
178 ).varstatus
179 # rev
180 self.getVariable(self.varrev[T - 1]).varstatus = self.getVariable(
181 self.varrev[T - 2]
182 ).varstatus
183
184 # adding the objective
185 objcoeff = [pow(1.05, 1 - (t + 1)) for t in range(T)]
186 objnlflag = [0] * T
187 objidx = self.addConstraint(
188 co.ConstraintType_Free, 0.0, self.varrev, objcoeff, objnlflag
189 )
190
191 # adding the constraints to the model
192 for t in range(T):
193 if t == 0:
194 considx = self.addConstraint(
195 co.ConstraintType_Eq,
196 demand[t] + 0.87 * 18.0,
197 [self.vartd[t], self.varp[t]], # the variables
198 [1.0, 0.13], # the coefficients
199 [0, 0], # nlflags
200 )
201 else:
202 considx = self.addConstraint(
203 co.ConstraintType_Eq,
204 demand[t],
205 [self.vartd[t], self.vartd[t - 1], self.varp[t]],
206 [1.0, -0.87, 0.13],
207 [0, 0, 0],
208 )
209 self.consttdeq.append(considx)
210
211 if t == 0:
212 considx = self.addConstraint(
213 co.ConstraintType_Eq,
214 0.75 * 6.5,
215 [self.vars[t], self.varp[t], self.varcs[t]],
216 [1.0, 0.0, 0.0],
217 [0, 1, 1],
218 )
219 else:
220 considx = self.addConstraint(
221 co.ConstraintType_Eq,
222 0.0,
223 [
224 self.vars[t],
225 self.vars[t - 1],
226 self.varp[t],
227 self.varcs[t],
228 ],
229 [1.0, -0.75, 0.0, 0.0],
230 [0, 0, 1, 1],
231 )
232 self.constseq.append(considx)
233 self.consmapping[considx] = (self.CONS_SEQ, t)
234
235 if t == 0:
236 considx = self.addConstraint(
237 co.ConstraintType_Eq,
238 0.0,
239 [self.varcs[t], self.vars[t]],
240 [1.0, -1.0],
241 [0, 0],
242 )
243 else:
244 considx = self.addConstraint(
245 co.ConstraintType_Eq,
246 0.0,
247 [self.varcs[t], self.varcs[t - 1], self.vars[t]],
248 [1.0, -1.0, -1.0],
249 [0, 0, 0],
250 )
251 self.constcseq.append(considx)
252
253 considx = self.addConstraint(
254 co.ConstraintType_Eq,
255 0.0,
256 [self.vard[t], self.vartd[t], self.vars[t]],
257 [1.0, -1.0, 1.0],
258 [0, 0, 0],
259 )
260 self.constdeq.append(considx)
261
262 if t == 0:
263 considx = self.addConstraint(
264 co.ConstraintType_Eq,
265 500.0,
266 [self.varr[t], self.vard[t]],
267 [1.0, 1.0],
268 [0, 0],
269 )
270 else:
271 considx = self.addConstraint(
272 co.ConstraintType_Eq,
273 0.0,
274 [self.varr[t], self.varr[t - 1], self.vard[t]],
275 [1.0, -1.0, 1.0],
276 [0, 0, 0],
277 )
278 self.constreq.append(considx)
279
280 considx = self.addConstraint(
281 co.ConstraintType_Eq,
282 0.0,
283 [self.varrev[t], self.vard[t], self.varp[t], self.varr[t]],
284 [1.0, 0.0, 0.0, 0.0],
285 [0, 1, 1, 1],
286 )
287 self.constdrev.append(considx)
288 self.consmapping[considx] = (self.CONS_DREV, t)
289
290 # Restore values from previous runs
291 if len(estat) > 0:
292 # the equation status
293 for i in range(len(estat)):
294 self.getConstraint(i).slackstatus = estat[i]
295
296 # tdeq
297 self.getConstraint(
298 self.consttdeq[T - 1]
299 ).slackstatus = self.getConstraint(self.consttdeq[T - 2]).slackstatus
300 # seq
301 self.getConstraint(
302 self.constseq[T - 1]
303 ).slackstatus = self.getConstraint(self.constseq[T - 2]).slackstatus
304 # cseq
305 self.getConstraint(
306 self.constcseq[T - 1]
307 ).slackstatus = self.getConstraint(self.constcseq[T - 2]).slackstatus
308 # deq
309 self.getConstraint(
310 self.constdeq[T - 1]
311 ).slackstatus = self.getConstraint(self.constdeq[T - 2]).slackstatus
312 # req
313 self.getConstraint(
314 self.constreq[T - 1]
315 ).slackstatus = self.getConstraint(self.constreq[T - 2]).slackstatus
316 # drev
317 self.getConstraint(
318 self.constdrev[T - 1]
319 ).slackstatus = self.getConstraint(self.constdrev[T - 2]).slackstatus
320
321 # setting the objective constraint
322 self.setObjectiveElement(co.ObjectiveElement_Constraint, objidx)
323
324 # setting the optimisation direction
325 self.setOptimizationSense(co.Sense_Maximize)
326
327 hsrow = []
328 hscol = []
329
330 for t in range(T):
331 # storing the start index for the hessian in each time period.
332 # This will provide a mapping that can be used in the hessian
333 # evaluation
334 self.hessianstart.append(len(hscol))
335
336 hscol.append(self.varcs[t])
337 hsrow.append(self.varcs[t])
338 hscol.append(self.varcs[t])
339 hsrow.append(self.varp[t])
340
341 # the row index for this entry is an error.
342 hscol.append(self.vard[t])
343 hsrow.append(self.vard[t])
344
345 # these entries are correct
346 hscol.append(self.vard[t])
347 hsrow.append(self.varp[t])
348 hscol.append(self.varr[t])
349 hsrow.append(self.varr[t])
350
351 self.setSDLagrangianStructure(hsrow, hscol)
352
353 def evaluateNonlinearTerm(self, x, rowno, ignerr, thread):
354 """
355 @copydoc conopt.ModelData.evaluateNonlinearTerm
356 @ingroup PYTHON1THREAD_PINADD2ERR
357 """
358 if rowno not in self.consmapping:
359 return
360
361 # unpack consset and time index
362 consset, t = self.consmapping[rowno]
363 assert consset in (self.CONS_SEQ, self.CONS_DREV)
364
365 g = 0
366 if consset == self.CONS_SEQ:
367 h1 = 1.1 + 0.1 * x[self.varp[t]]
368 h2 = pow(1.02, -x[self.varcs[t]] / 7.0)
369 g = -h1 * h2
370
371 elif consset == self.CONS_DREV:
372 g = -x[self.vard[t]] * (x[self.varp[t]] - 250.0 / x[self.varr[t]])
373
374 return g
375
376 def evaluateNonlinearJacobian(self, x, rowno, jacnum, ignerr, thread):
377 """
378 @copydoc conopt.ModelData.evaluateNonlinearJacobian
379 @ingroup PYTHON1THREAD_PINADD2ERR
380 """
381 if rowno not in self.consmapping:
382 return
383
384 # unpack consset and time index
385 consset, t = self.consmapping[rowno]
386 assert consset in (self.CONS_SEQ, self.CONS_DREV)
387
388 jac = []
389 if consset == self.CONS_SEQ:
390 h1 = 1.1 + 0.1 * x[self.varp[t]]
391 h2 = pow(1.02, -x[self.varcs[t]] / 7.0)
392 jac.append(h1 * h2 * math.log(1.02) / 7.0)
393 jac.append(-h2 * 0.1)
394
395 elif consset == self.CONS_DREV:
396 jac.append(-(x[self.varp[t]] - 250.0 / x[self.varr[t]]))
397 jac.append(-x[self.vard[t]] * 250.0 / pow(x[self.varr[t]], 2))
398 jac.append(-x[self.vard[t]])
399
400 return jac
401
402 def evaluateSDLagrangian(self, x, u, hessianrow, hessiancol):
403 """
404 @copydoc conopt.ModelData.evaluateSDLagrangian
405 @ingroup PYTHON1THREAD_PINADD2ERR
406 """
407 numhessian = len(hessianrow)
408 hessian = [0 for i in range(numhessian)]
409
410 # Normal Evaluation mode
411 for t in range(self.T):
412 hessidx = self.hessianstart[t]
413 cs = x[self.varcs[t]]
414 d = x[self.vard[t]]
415 r = x[self.varr[t]]
416 p = x[self.varp[t]]
417
418 h1 = 1.1 + 0.1 * p
419 h2 = pow(1.02, -cs / 7.0)
420
421 # the second derivative of the SEQ equations
422 hessian[hessidx] = (
423 -h1 * h2 * pow(math.log(1.02) / 7.0, 2) * u[self.constseq[t]]
424 )
425 hessian[hessidx + 1] = (
426 0.1 * h2 * (math.log(1.02) / 7.0) * u[self.constseq[t]]
427 )
428
429 # the second derivative of the DREV equations
430 hessian[hessidx + 2] = -250.0 / pow(r, 2) * u[self.constdrev[t]]
431 hessian[hessidx + 3] = -1.0 * u[self.constdrev[t]]
432 hessian[hessidx + 4] = 500.0 * d / pow(r, 3) * u[self.constdrev[t]]
433
434 return hessian
435
436
437if __name__ == '__main__':
438 name = os.path.basename(__file__)[:-3]
439
440 conopt = co.Conopt(name)
441 msghdlr = std.TutMessageHandler(name)
442 conopt.setMessageHandler(msghdlr)
443
444 # Turn debugging of FDEval on or off: -1 means first call only
445 conopt.debugFV(-1)
446
447 # getting the license variables
448 license_int_1 = os.environ.get('CONOPT_LICENSE_INT_1', None)
449 license_int_2 = os.environ.get('CONOPT_LICENSE_INT_2', None)
450 license_int_3 = os.environ.get('CONOPT_LICENSE_INT_3', None)
451 license_text = os.environ.get('CONOPT_LICENSE_TEXT', None)
452 if (
453 license_int_1 is not None
454 and license_int_2 is not None
455 and license_int_3 is not None
456 and license_text is not None
457 ):
458 conopt.setLicense(
459 int(license_int_1),
460 int(license_int_2),
461 int(license_int_3),
462 license_text,
463 )
464
465 expected_sols = [1225.86, 1280.28, 1333.74, 1386.24]
466 for i in range(16, 21):
467 model = PinAdd2ErrModelData()
468
469 xkeep = conopt.getVariableValues()
470 xstat = conopt.getVariableStatus()
471 estat = conopt.getConstraintStatus()
472
473 # building the model
474 model.buildModel(
475 i, xkeep, xstat, estat
476 ) # Add T, and other vectors as parameters
477
478 conopt.loadModel(model)
479
480 if i == 16:
481 # We have an intentional error in Pin_2DLagrStr and we have turned debugging on.
482 # COI_Solve should still return 0, but the Solver status should return 5, Evaluation
483 # Error Limit, and the Model status should be Intermediate Infeasible (6) or Intermediate
484 # Nonoptimal (7).
485 conopt.debug2D(-1)
486
487 coi_error = conopt.solve()
488
489 conopt.sendMessage(f'After solving. COI_Error = {coi_error}')
490
491 if coi_error:
492 std.python_log(
493 conopt, f'Errors encountered during first solve {coi_error}'
494 )
495
496 elif (
497 (conopt.modelStatus() < 6)
498 or (conopt.modelStatus() > 7)
499 or conopt.solutionStatus() != 5
500 ):
501 std.python_log(
502 conopt,
503 'Incorrect Model or Solver Status during first solve',
504 )
505
506 conopt.printStatus()
507
508 else:
509 # Turn debugging of 2nd derivative off again.
510 # We should expect a proper optimal solution even with an incorrect
511 # 2DLagr routine so we test for (local) optimality after the solve.
512 conopt.debug2D(0)
513 coi_error = conopt.solve()
514
515 conopt.sendMessage(f'After solving. COI_Error = {coi_error}')
516
517 if (conopt.modelStatus() != 2) or (conopt.solutionStatus() != 1):
518 std.python_log(conopt, 'Incorrect Model or Solver Status')
519
520 retcode = std.checkSolve(
521 conopt, expected_sols[i - 17], coi_error, tol=0.01
522 )
523
524 sys.exit(retcode)
static int checkSolve(String name, int model_status, int solution_status, double objective, double expected_objective, double tol)
Definition std.java:20
evaluateNonlinearTerm(self, x, rowno, ignerr, thread)
callback method for evaluating the nonlinear terms in a given row
evaluateNonlinearJacobian(self, x, rowno, jacnum, ignerr, thread)
callback method for evaluating the jacobian for the nonlinear terms in a given row
buildModel(self, T, xkeep, xstat, estat)
adding the variables and constraints to the model
Definition pinadd2err.py:49
evaluateSDLagrangian(self, x, u, hessianrow, hessiancol)
Computes and returns the numerical values of the Lagrangian of the Hessian.