gamspy-base 52.0.0rc1__py3-none-macosx_13_0_x86_64.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.
Files changed (60) hide show
  1. gamspy_base/__init__.py +18 -0
  2. gamspy_base/eula.pdf +0 -0
  3. gamspy_base/gams +0 -0
  4. gamspy_base/gamscmex.out +0 -0
  5. gamspy_base/gamserrs.txt +1318 -0
  6. gamspy_base/gamsgetkey +0 -0
  7. gamspy_base/gamslice.txt +8 -0
  8. gamspy_base/gamsprobe +0 -0
  9. gamspy_base/gamsstmp.txt +1 -0
  10. gamspy_base/gdxdiff +0 -0
  11. gamspy_base/gdxdump +0 -0
  12. gamspy_base/gevopt.def +102 -0
  13. gamspy_base/gmscmpun.txt +56 -0
  14. gamspy_base/gmscvnus.run +4 -0
  15. gamspy_base/gmscvnux.out +0 -0
  16. gamspy_base/gmsgenus.run +4 -0
  17. gamspy_base/gmsgenux.out +0 -0
  18. gamspy_base/gmske_us.run +11 -0
  19. gamspy_base/gmske_ux.out +912 -0
  20. gamspy_base/gmsprmun.txt +12 -0
  21. gamspy_base/gmssb_us.run +4 -0
  22. gamspy_base/gmssb_ux.out +0 -0
  23. gamspy_base/libco4cclib64.dylib +0 -0
  24. gamspy_base/libconopt464.dylib +0 -0
  25. gamspy_base/libconoptlu.dylib +0 -0
  26. gamspy_base/libcplex2212.dylib +0 -0
  27. gamspy_base/libcpxcclib64.dylib +0 -0
  28. gamspy_base/libcrypto.3.dylib +0 -0
  29. gamspy_base/libcvdcclib64.dylib +0 -0
  30. gamspy_base/libdctmdclib64.dylib +0 -0
  31. gamspy_base/libgcc_s.1.1.dylib +0 -0
  32. gamspy_base/libgdxcclib64.dylib +0 -0
  33. gamspy_base/libgdxdclib64.dylib +0 -0
  34. gamspy_base/libgfortran.5.dylib +0 -0
  35. gamspy_base/libgmdcclib64.dylib +0 -0
  36. gamspy_base/libgmszlib164.dylib +0 -0
  37. gamspy_base/libgomp.1.dylib +0 -0
  38. gamspy_base/libgsscclib64.dylib +0 -0
  39. gamspy_base/libguccclib64.dylib +0 -0
  40. gamspy_base/libjoatdclib64.dylib +0 -0
  41. gamspy_base/liboptdclib64.dylib +0 -0
  42. gamspy_base/libpath52.dylib +0 -0
  43. gamspy_base/libptccclib64.dylib +0 -0
  44. gamspy_base/libquadmath.0.dylib +0 -0
  45. gamspy_base/libssl.3.dylib +0 -0
  46. gamspy_base/libstdc++.6.dylib +0 -0
  47. gamspy_base/mps2gms +0 -0
  48. gamspy_base/optconopt.def +351 -0
  49. gamspy_base/optconvert.def +126 -0
  50. gamspy_base/optcplex.def +880 -0
  51. gamspy_base/optgams.def +724 -0
  52. gamspy_base/optnlpec.def +69 -0
  53. gamspy_base/optpath.def +231 -0
  54. gamspy_base/optsbb.def +94 -0
  55. gamspy_base/version.py +1 -0
  56. gamspy_base-52.0.0rc1.dist-info/METADATA +65 -0
  57. gamspy_base-52.0.0rc1.dist-info/RECORD +60 -0
  58. gamspy_base-52.0.0rc1.dist-info/WHEEL +5 -0
  59. gamspy_base-52.0.0rc1.dist-info/top_level.txt +1 -0
  60. minigams-env-3.129d2d53b5/lib/python3.12/site-packages/minigams/GAMSPY_BASE_README.md +58 -0
@@ -0,0 +1,912 @@
1
+ #
2
+ #MIT License
3
+ #
4
+ #Copyright (c) 2020 NEOS-Server
5
+ #
6
+ #Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ #of this software and associated documentation files (the "Software"), to deal
8
+ #in the Software without restriction, including without limitation the rights
9
+ #to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ #copies of the Software, and to permit persons to whom the Software is
11
+ #furnished to do so, subject to the following conditions:
12
+ #
13
+ #The above copyright notice and this permission notice shall be included in all
14
+ #copies or substantial portions of the Software.
15
+ #
16
+ #THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ #IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ #FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ #AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ #LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ #OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ #SOFTWARE.
23
+ #
24
+
25
+ import os
26
+ import linecache
27
+ try:
28
+ import zipextimporter
29
+ except:
30
+ pass
31
+ import re
32
+ import xmlrpc.client
33
+ import sys
34
+ import time
35
+ import socket
36
+ import base64
37
+ import gzip
38
+ import io
39
+ import xml.dom.minidom
40
+ import string
41
+ import pathlib
42
+ import ssl
43
+ import certifi
44
+
45
+ solverMap = {}
46
+ solverMap[ 1] = 'cbc' # lp
47
+ solverMap[ 2] = 'cbc' # mip
48
+ solverMap[ 3] = 'cbc' # rmip
49
+ solverMap[ 4] = 'ipopt' # nlp
50
+ solverMap[ 5] = 'path' # mcp
51
+ solverMap[ 6] = 'nlpec' # mpec
52
+ solverMap[ 7] = 'nlpec' # rmpec
53
+ solverMap[ 8] = 'path' # cns
54
+ solverMap[ 9] = 'ipopt' # dnlp
55
+ solverMap[10] = 'ipopt' # rminlp
56
+ solverMap[11] = 'shot' # minlp
57
+ solverMap[12] = 'ipopt' # qcp
58
+ solverMap[13] = 'shot' # miqcp
59
+ solverMap[14] = 'ipopt' # rmiqcp
60
+ solverMap[15] = 'jams' # emp
61
+
62
+ class KestrelException(Exception):
63
+ def __init__(self,msg):
64
+ Exception.__init__(self)
65
+ self.msg = msg
66
+
67
+ def __str__(self):
68
+ return repr(self.msg)
69
+
70
+ class KestrelSolverException(KestrelException):
71
+ def __init__(self,msg,solverlist):
72
+ KestrelException.__init__(self,msg)
73
+ self.msg += "\nCreate options file and include the following lines:\n\tkestrel_solver <solvername>\n"
74
+ self.msg += "\tneos_server <hostname>[:<port>]\n\n"
75
+ self.msg += "The following solvers are available on NEOS:\n"
76
+ for solver in solverlist:
77
+ self.msg += solver.upper() +"\n"
78
+
79
+ class KestrelGamsClient:
80
+ def __init__(self,argv):
81
+ self.argv=argv
82
+ self.serverProtocol="https"
83
+ self.serverHost="neos-server.org"
84
+ self.serverPort=3333
85
+ self.solverName=None
86
+ self.jobNumber=None
87
+ self.password=None
88
+ self.priority="long"
89
+ self.socket_timeout=0
90
+ self.authUsername=None
91
+ self.authUserPassword=None
92
+
93
+ # action-parameter is outdated
94
+ '''
95
+ if len(self.argv) >= 3:
96
+ self.cntrfile = self.argv[2]
97
+ self.action = self.argv[1].lower()
98
+ if self.action not in ['kill','retrieve','submit','solve']:
99
+ self.Usage()
100
+ else:
101
+ self.Usage()
102
+ '''
103
+
104
+ if len(self.argv) >= 2:
105
+ self.cntrfile = self.argv[1]
106
+ self.action = 'solve'
107
+ else:
108
+ self.Usage()
109
+
110
+ def Usage(self):
111
+ sys.stderr.write("\n--- Kestrel fatal error: usage\n")
112
+ sys.stderr.write(" gamske_ux.out <cntrfile>\n")
113
+ sys.exit(1)
114
+
115
+ def Fatal(self, str):
116
+ sys.stderr.write("\n--- Kestrel fatal error: %s\n\n" % str)
117
+ sys.exit(1)
118
+
119
+ def Error(self, str):
120
+ if self.logopt in [1,3,4]:
121
+ # Write the message to standard output
122
+ sys.stdout.write("\n--- Kestrel error: %s\n\n" % str)
123
+
124
+ if self.logopt in [2,4]:
125
+ # Append the error message to the logfile indicated
126
+ try:
127
+ f = open(self.logfilename,'a')
128
+ f.write("\n--- Kestrel error: %s\n\n" % str)
129
+ f.close()
130
+ except IOError as e:
131
+ self.Fatal("Could not append to log file %s" % self.logfilename)
132
+
133
+ try:
134
+ f = open(self.statfilename,'a')
135
+ f.write("=1\n\n--- Kestrel error: %s\n\n=2\n" % str)
136
+ f.close()
137
+ except IOError as e:
138
+ self.Fatal("Could not append to status file %s\n" % self.statfilename)
139
+
140
+ sys.exit(0)
141
+
142
+ def getDefaultEmail(self):
143
+ if 'NEOS_EMAIL' in os.environ:
144
+ return os.environ['NEOS_EMAIL']
145
+ return None
146
+
147
+ def parseControlFile(self):
148
+ """
149
+ This function does the following with the cntr file
150
+ line 13:
151
+ extract isAscii, useOptions
152
+
153
+ line 18:
154
+ matrix file, save and change to gamsmatr.scr
155
+
156
+ line 19:
157
+ instruction file; save and change to gamsinst.scr
158
+
159
+ line 20:
160
+ set options file to 'kestrel.opt'
161
+
162
+ line 21:
163
+ status file; save and change to gamsstat.scr
164
+
165
+ line 22:
166
+ solution file; save and change to gamssolu.scr
167
+
168
+ line 23:
169
+ log file; save and remove absolute path
170
+
171
+ line 24:
172
+ dictionary file; save and change to gamsdict.scr
173
+
174
+ line 25:
175
+ set to '2' to write to log file
176
+
177
+ line 28-30:
178
+ set working,system,scratch directories to '.'
179
+
180
+ line 33,34,35:
181
+ remove license
182
+
183
+ line 37:
184
+ set parameter file
185
+
186
+ line 38:
187
+ read #models #solvers
188
+ ignore next #models + 2*#solvers with (SOLVER # # 0 ..) +
189
+ 3*#solvers with (SOLVER # # 1 ...) lines
190
+
191
+ next two lines are more license (remove them)
192
+
193
+ set directories of remaining paths to current directory
194
+ (.scr, .so, sbbinfo.)
195
+
196
+ change the scratch file extension to 'scr'
197
+ """
198
+
199
+ try:
200
+ f = open(self.cntrfile,'r')
201
+ lines = f.readlines()
202
+ f.close()
203
+ except IOError as e:
204
+ self.Fatal("Could not open control file %s" % self.cntrfile)
205
+
206
+ # extract control version number
207
+ self.cntver = 0
208
+ m = re.match(r'(\d+)',lines[0])
209
+ if m and m.groups():
210
+ self.cntver = int(m.groups()[0])
211
+
212
+ self.modeltype = int(lines[1].split()[0])
213
+
214
+ if self.cntver not in [42, 44, 46, 47, 48, 49, 50, 51, 52, 53]:
215
+ self.Fatal("GAMS cntr-file version 42, 44, 46, 47, 48, 49, 50, 51, 52, 53 required")
216
+
217
+ # extract isAscii, useOptions
218
+ m = re.match(r'(\d+)\s+(\d+)',lines[12])
219
+ if m and m.groups():
220
+ self.isAscii=m.groups()[0]
221
+ self.useOptions = int(m.groups()[1])
222
+ else:
223
+ self.Fatal("Line 13 of the control file is incorrect")
224
+
225
+ # is this is an MPSGE model?
226
+ self.isMPSGE = int(lines[15].split()[0])
227
+
228
+ # get the matrix and instruction scratch files and patch
229
+ self.matrfilename = lines[17].strip()
230
+ lines[17] = "gamsmatr.scr\n"
231
+
232
+ self.instfilename = lines[18].strip()
233
+ lines[18] = "gamsinst.scr\n"
234
+
235
+ # patch option file name; always use kestrel.opt
236
+ self.optfilename = ""
237
+ m = re.match(r'(.*)kestrel.*\.(.*)',lines[19])
238
+ if m and m.groups():
239
+ self.optfilename = m.groups()[0] + "kestrel." + m.groups()[1]
240
+ lines[19] = "kestrel.opt\n"
241
+
242
+ # get the status and solution scratch files and patch
243
+ self.statfilename = lines[20].strip()
244
+ lines[20] = "gamsstat.scr\n"
245
+
246
+ self.solufilename = lines[21].strip()
247
+ lines[21] = "gamssolu.scr\n"
248
+
249
+ # get the log filename and patch
250
+ self.logfilename = lines[22].strip()
251
+ lines[22] = "gamslog.scr\n"
252
+
253
+ # get the dictionary filename and patch
254
+ self.dictfilename = lines[23].strip()
255
+ lines[23] = "gamsdict.scr\n"
256
+
257
+ # get the logfile option, then make output written to logfile
258
+ m = re.match(r'(\d+)',lines[24])
259
+ if m and m.groups():
260
+ self.logopt = int(m.groups()[0])
261
+ lines[24]="2\n"
262
+
263
+ # set working, system, and scratch directories
264
+ self.scrdir = lines[29].strip()
265
+ lines[27] = lines[28] = lines[29] = '.\n'
266
+
267
+ # remove first part of license
268
+ lines[32] = lines[33] = lines[34] = "\n"
269
+
270
+ # patch parameter file
271
+ lines[36] = "gmsprmun.scr"
272
+
273
+ # downgrade the cntr-file version 53 to 52
274
+ if self.cntver == 53:
275
+ lines[0] = "52\n"
276
+ # remove seventh and eigth license line, license gets replaced anyway
277
+ del lines[-18]
278
+ del lines[-18]
279
+ self.cntver = 52
280
+
281
+ # downgrade the cntr-file version 52 to 51
282
+ if self.cntver == 52:
283
+ # We do not yet handle models with more than INT_MAX NNZ in Kestrel
284
+ lines[0] = "51\n"
285
+ # remove final line of CF (u+15, rvec[ 28] rvec[ 29]
286
+ lines = lines[:-1]
287
+ self.cntver = 51
288
+
289
+ # downgrade the cntr-file version 51 to 50
290
+ if self.cntver == 51:
291
+ # remove last number (savepoint) of this line
292
+ lines[13] = lines[13].rpartition(' ')[0] + "\n"
293
+ # 51 -> 50
294
+ lines[0] = "50\n"
295
+ self.cntver = 50
296
+
297
+ # downgrade the cntr-file version 50 to 49 // No change required since this was in the license section which does not get copied
298
+ if self.cntver == 50:
299
+ # 50 -> 49
300
+ lines[0] = "49\n"
301
+ self.cntver = 49
302
+
303
+ # downgrade the cntr-file version 49 to 48 // No change required since this was in the license section which does not get copied
304
+ if self.cntver == 49:
305
+ # 49 -> 48
306
+ lines[0] = "48\n"
307
+ self.cntver = 48
308
+
309
+ # downgrade the cntr-file version 48 to 47
310
+ if self.cntver == 48:
311
+ # 48 -> 47
312
+ lines[0] = "47\n"
313
+ self.cntver = 47
314
+ # remove last two numbers of this line
315
+ lines[13] = lines[13].rpartition(' ')[0] + "\n"
316
+ lines[13] = lines[13].rpartition(' ')[0] + "\n"
317
+
318
+ # downgrade the cntr-file version 47 to 46
319
+ if self.cntver == 47:
320
+ # 47 -> 46
321
+ lines[0] = "46\n"
322
+ self.cntver = 46
323
+ # remove line with file name
324
+ lines = lines[:-2]
325
+ lines.append("")
326
+
327
+ # downgrade the cntr-file version 46 to 42
328
+ # no support for threads, external funclib and guss
329
+ if self.cntver == 46:
330
+ # 46 -> 42
331
+ lines[0] = "42\n"
332
+
333
+ # remove last number of this line
334
+ lines[2] = lines[2].rpartition(' ')[0] + "\n"
335
+
336
+ # remove threads-option
337
+ lines[13] = lines[13].rpartition(' ')[0] + "\n"
338
+
339
+ # remove last two lines
340
+ lines = lines[:-2]
341
+
342
+ # treat the cntr-file now like a version 42 one
343
+ self.cntver = 42
344
+
345
+ # downgrade the cntr-file version 44 to 42
346
+ elif self.cntver == 44:
347
+ # 44 -> 42
348
+ lines[0] = "42\n"
349
+
350
+ # remove threads-option
351
+ lines[13] = lines[13].rpartition(' ')[0] + "\n"
352
+
353
+ # remove last line
354
+ lines = lines[:-1]
355
+
356
+ # treat the cntr-file now like a version 42 one
357
+ self.cntver = 42
358
+
359
+ if self.cntver == 42:
360
+ # remove second part of license
361
+ lines[-13] = lines[-12] = "\n"
362
+
363
+ # make everything in local directory
364
+ lines[-11] = 'model.scr\n'
365
+ lines[-6] = 'model.so\n'
366
+ lines[-5] = 'sbbinfo.scr\n'
367
+ lines[-4] = 'gamscntr.scr\n'
368
+ lines[-3] = './\n'
369
+
370
+ # patch scratch file extension
371
+ self.scrext = lines[-2].strip()
372
+ lines[-2] = 'scr\n'
373
+
374
+ # get the entire control file name
375
+ self.cntr = "".join(lines[:37]) + "".join(lines[-13:])
376
+
377
+ def writeErrorOutputFiles(self):
378
+ """
379
+ This writes solution and status files returned when an error occurs.
380
+ """
381
+
382
+ try:
383
+ f = open(self.statfilename,"w")
384
+ f.write("""=0 Kestrel\n""")
385
+ f.close()
386
+ except IOError as e:
387
+ self.Error("Could not initialize status file %s\n" % self.statfilename)
388
+
389
+ try:
390
+ f = open(self.solufilename,"w")
391
+ f.write(""" 1 6.0000000000000000E+00
392
+ 2 1.3000000000000000E+01
393
+ 3 0.0000000000000000E+00
394
+ 4 0.0
395
+ 5 0.0000000000000000E+00
396
+ 6 0.0
397
+ 7 0.0
398
+ 8 0.0
399
+ 0 0.0\n""")
400
+ f.close()
401
+
402
+ except IOError as e:
403
+ self.Error("Could not open solution file %s\n" % self.solufilename)
404
+
405
+ def writeLog(self, text):
406
+ if self.logopt in [1,3,4]:
407
+ sys.stdout.write(text)
408
+ if self.logopt in [2,4]:
409
+ try:
410
+ f = open(self.logfilename,'a')
411
+ f.write(text)
412
+ f.close()
413
+ except IOError as e:
414
+ self.Fatal("Could not append to log file %s" % self.logfilename)
415
+
416
+ def parseOptionsFile(self):
417
+ if (self.useOptions == 0):
418
+ # raise KestrelSolverException("No options file indicated\n",self.kestrelGamsSolvers)
419
+ self.solverName = solverMap[self.modeltype]
420
+ elif os.access(self.optfilename,os.R_OK):
421
+ optfile = open(self.optfilename,'r')
422
+ self.writeLog("Reading parameter(s) from \"" + self.optfilename + "\"\n")
423
+ for line in optfile:
424
+ m = re.match(r'neos_user_password[\s=]+(\S+)',line)
425
+ if m:
426
+ self.writeLog(">> neos_user_password ******")
427
+ else:
428
+ self.writeLog(">> " + line)
429
+
430
+ m = re.match(r'kestrel_priority[\s=]+(\S+)',line)
431
+ if m:
432
+ value = m.groups()[0]
433
+ if value.lower()=="short":
434
+ self.priority = "short"
435
+
436
+ m = re.match(r'kestrel_solver[\s=]+(\S+)',line)
437
+ if m:
438
+ self.solverName = m.groups()[0]
439
+
440
+ m = re.match(r'neos_server[\s=]+(\S+)://(\S+):(\d+)',line)
441
+ if m:
442
+ self.serverProtocol = m.groups()[0]
443
+ self.serverHost = m.groups()[1]
444
+ self.serverPort = m.groups()[2]
445
+
446
+ m = re.match(r'neos_username[\s=]+(\S+)',line)
447
+ if m:
448
+ self.authUsername = m.groups()[0]
449
+
450
+ m = re.match(r'neos_user_password[\s=]+(\S+)',line)
451
+ if m:
452
+ self.authUserPassword = m.groups()[0]
453
+
454
+ elif re.match(r'neos_server[\s=]+(\S+)://(\S+)',line):
455
+ m = re.match(r'neos_server[\s=]+(\S+)://(\S+)',line)
456
+ self.serverProtocol = m.groups()[0]
457
+ self.serverHost = m.groups()[1]
458
+
459
+ elif re.match(r'neos_server[\s=]+(\S+):(\d+)',line):
460
+ m = re.match(r'neos_server[\s=]+(\S+):(\d+)',line)
461
+ self.serverHost = m.groups()[0]
462
+ self.serverPort = m.groups()[1]
463
+
464
+ else:
465
+ m = re.match(r'neos_server[\s=]+(\S+)',line)
466
+ if m:
467
+ self.serverHost = m.groups()[0]
468
+
469
+ m = re.match(r'kestrel_(job|jobnumber|jobNumber)[\s=]+(\d+)', line)
470
+ if m:
471
+ self.jobNumber=int(m.groups()[1])
472
+
473
+ m = re.match(r'kestrel_(pass|password)[\s=]+(\S+)', line)
474
+ if m:
475
+ self.password = m.groups()[1]
476
+
477
+ m = re.match(r'socket_timeout[\s=]+(\d+)',line)
478
+ if m:
479
+ self.socket_timeout = m.groups()[0]
480
+ socket.setdefaulttimeout(float(self.socket_timeout))
481
+
482
+ optfile.close()
483
+ self.writeLog("\nFinished reading from \"" + self.optfilename + "\"\n")
484
+ else:
485
+ raise KestrelSolverException("Could not read options file %s\n" % self.optfilename,self.kestrelGamsSolvers)
486
+
487
+ def connectServer(self):
488
+ if self.logopt in [1,3,4]:
489
+ sys.stdout.write("Connecting to: %s://%s:%s\n" % (self.serverProtocol,self.serverHost,self.serverPort))
490
+ if self.logopt in [2,4]:
491
+ # Append the message to the logfile indicated
492
+ try:
493
+ f = open(kestrel.logfilename,'a')
494
+ f.write("Connecting to: %s://%s:%s\n" % (self.serverProtocol,self.serverHost,self.serverPort))
495
+ f.close()
496
+ except IOError as e:
497
+ self.Fatal("Could not append to log file %s" % self.logfilename)
498
+ ssl_context = ssl.create_default_context()
499
+ if ssl_context.minimum_version < ssl.TLSVersion.TLSv1_2:
500
+ ssl_context.minimum_version = ssl.TLSVersion.TLSv1_2
501
+ if sys.platform == "win32":
502
+ ssl_context.load_verify_locations(certifi.where())
503
+ self.neos = xmlrpc.client.Server("%s://%s:%s" % (self.serverProtocol,self.serverHost,self.serverPort), context=ssl_context)
504
+
505
+ reply = self.neos.ping()
506
+ if reply.find('alive') < 0:
507
+ raise KestrelException("Unable to contact NEOS at https://%s:%d" % \
508
+ (self.host, self.port))
509
+
510
+ def obtainSolvers(self):
511
+ # Form a list of all kestrel-gams solver available on NEOS
512
+ allKestrelSolvers = self.neos.listSolversInCategory("kestrel")
513
+ self.kestrelGamsSolvers = []
514
+ for s in allKestrelSolvers:
515
+ i = s.find(':GAMS')
516
+ if i > 0:
517
+ self.kestrelGamsSolvers.append(s[0:i])
518
+
519
+ def checkOptionsFile(self):
520
+ if self.solverName and (self.solverName.lower() not in [s.lower() for s in self.kestrelGamsSolvers]):
521
+ errmsg = "Solver '%s' not available on NEOS.\n" % self.solverName
522
+ raise KestrelSolverException(errmsg, self.kestrelGamsSolvers)
523
+
524
+ def formSubmission(self):
525
+ if not self.solverName:
526
+ raise KestrelSolverException("No 'kestrel_solver' option found in option file\n",self.kestrelGamsSolvers)
527
+
528
+ # Get the matrix, dictionary and instruction file
529
+ gamsFiles = {}
530
+ gamsFiles['cntr'] = io.BytesIO(self.cntr.encode())
531
+
532
+ # Need to read empinfo.dat or empinfo.scr
533
+ empInfoFileName = os.path.join(self.scrdir, "empinfo." + self.scrext)
534
+ if os.access(empInfoFileName,os.R_OK):
535
+ gamsFiles['empinfo'] = io.BytesIO()
536
+ f = open(empInfoFileName,"rb")
537
+ zipper = gzip.GzipFile(mode='wb',fileobj=gamsFiles['empinfo'])
538
+ zipper.write(f.read())
539
+ zipper.close()
540
+ f.close()
541
+
542
+ # Need to read scenarios
543
+ scenDictName = os.path.join(self.scrdir, "scenario_dict." + self.scrext)
544
+ if os.access(scenDictName,os.R_OK):
545
+ gamsFiles['scenario'] = io.BytesIO()
546
+ f = open(scenDictName,"rb")
547
+ zipper = gzip.GzipFile(mode='wb',fileobj=gamsFiles['scenario'])
548
+ zipper.write(f.read())
549
+ zipper.close()
550
+ f.close()
551
+
552
+ if os.access(self.matrfilename,os.R_OK):
553
+ gamsFiles['matr'] = io.BytesIO()
554
+ f = open(self.matrfilename,"rb")
555
+ zipper = gzip.GzipFile(mode='wb',fileobj=gamsFiles['matr'])
556
+ zipper.write(f.read())
557
+ zipper.close()
558
+ f.close()
559
+
560
+ if os.access(self.instfilename,os.R_OK):
561
+ gamsFiles['inst'] = io.BytesIO()
562
+ f = open(self.instfilename,"rb")
563
+ zipper = gzip.GzipFile(mode='wb',fileobj=gamsFiles['inst'])
564
+ zipper.write(f.read())
565
+ zipper.close()
566
+ f.close()
567
+
568
+ if os.access(self.dictfilename,os.R_OK):
569
+ gamsFiles['dict'] = io.BytesIO()
570
+ f = open(self.dictfilename,"rb")
571
+ zipper = gzip.GzipFile(mode='wb',fileobj=gamsFiles['dict'])
572
+ zipper.write(f.read())
573
+ zipper.close()
574
+ f.close()
575
+
576
+ if self.isMPSGE != 0 and self.modeltype == 5 and os.access(os.path.join(self.scrdir,'gedata.' + self.scrext),os.R_OK): # MCP might be an MPSGE model
577
+ gamsFiles['cge'] = io.BytesIO()
578
+ f = open(os.path.join(self.scrdir,'gedata.' + self.scrext),"rb")
579
+ zipper = gzip.GzipFile(mode='wb',fileobj=gamsFiles['cge'])
580
+ s=f.read()
581
+ end = s.find(b"gamsdict.")
582
+ if end != -1:
583
+ start = end
584
+ while ord(s[end]) != 32: #whitespace
585
+ end = end+1
586
+ while ord(s[start]) != 0:
587
+ start = start-1
588
+ orgStr = s[start+1:end]
589
+ replStr = b"./gamsdict.scr" + b" "*(len(orgStr) - len("./gamsdict.scr"))
590
+ s = s.replace(orgStr, replStr)
591
+ zipper.write(s)
592
+ zipper.close()
593
+ f.close()
594
+
595
+ self.xml = """
596
+ <document>
597
+ <category>kestrel</category>
598
+ <solver>%s</solver>
599
+ <inputType>GAMS</inputType>
600
+ <priority>%s</priority>
601
+ """ % (self.solverName,self.priority)
602
+
603
+ for key in list(gamsFiles.keys()):
604
+ self.xml += "<%s><base64>%s</base64></%s>\n" % (key,base64.b64encode(gamsFiles[key].getvalue()).decode(),key)
605
+ gamsFiles[key].close()
606
+
607
+ # Remove 'kestrel', 'neos' and 'socket_timeout' options from options file; they are not needed
608
+ email = None
609
+ xpressemail = None
610
+ runningtime = None
611
+ self.xml += "<options><![CDATA["
612
+ if self.useOptions:
613
+ with open(self.optfilename) as fp:
614
+ for line in fp.readlines():
615
+ if not re.match(r'kestrel|neos_server|neos_username|neos_user_password|email|xpressemail|runtime|socket_timeout',line):
616
+ self.xml += line
617
+ elif re.match(r'email',line):
618
+ email = line.rsplit()[1]
619
+ elif re.match(r'xpressemail',line):
620
+ xpressemail = line.rsplit()[1]
621
+ elif re.match(r'runtime',line):
622
+ runningtime = line.rsplit()[1]
623
+ self.xml += "]]></options>\n"
624
+
625
+ if not email:
626
+ email = self.getDefaultEmail()
627
+ if not email:
628
+ self.Error("No email address provided. Either specify it in an option file or set environment variable NEOS_EMAIL (e.g. via gamsconfig.yaml).")
629
+ self.xml += "<email>"
630
+ self.xml += email
631
+ self.xml += "</email>\n"
632
+
633
+ if xpressemail:
634
+ self.xml += "<xpressemail>"
635
+ self.xml += xpressemail
636
+ self.xml += "</xpressemail>\n"
637
+
638
+ if runningtime:
639
+ self.xml += "<priority>"
640
+ self.xml += runningtime
641
+ self.xml += "</priority>"
642
+
643
+ self.xml += "</document>"
644
+
645
+ def submit(self):
646
+ user = "%s on %s" % (os.getenv('LOGNAME'),
647
+ socket.getfqdn(socket.gethostname()))
648
+ if self.authUsername is None or self.authUserPassword is None:
649
+ if self.authUsername: self.writeLog("\nWarning: 'neos_username' was specified, but not 'neos_user_password'")
650
+ if self.authUserPassword: self.writeLog("\nWarning: 'neos_user_password' was specified, but not 'neos_username'")
651
+ (self.jobNumber,self.password) = \
652
+ self.neos.submitJob(self.xml,user,"kestrel")
653
+ else:
654
+ (self.jobNumber,self.password) = \
655
+ self.neos.authenticatedSubmitJob(self.xml,self.authUsername,self.authUserPassword,"kestrel")
656
+ if self.jobNumber==0:
657
+ raise KestrelException(self.password)
658
+
659
+ if self.logopt in [1,3,4]:
660
+ # Send the output to the screen
661
+ sys.stdout.write("\nNEOS job#=%d, pass=%s\n\n" % (self.jobNumber,self.password))
662
+ sys.stdout.write("Check the following URL for progress report :\n")
663
+ #sys.stdout.write("http://www-neos.mcs.anl.gov/cgi-bin/nph-neos-solver.cgi?admin=results&jobnumber=%d&pass=%s\n\n" % (self.jobNumber,self.password))
664
+ sys.stdout.write("%s://%s/neos/cgi-bin/nph-neos-solver.cgi?admin=results&jobnumber=%d&pass=%s\n\n" % (self.serverProtocol,self.serverHost,self.jobNumber,self.password))
665
+ if self.logopt in [2,4]:
666
+ # Append the error message to the logfile indicated
667
+ try:
668
+ f = open(self.logfilename,'a')
669
+ f.write("\nNEOS job#=%d, pass=%s\n\n" % (self.jobNumber,self.password))
670
+ f.write("Check the following URL for progress report :\n")
671
+ f.write("%s://%s/neos/cgi-bin/nph-neos-solver.cgi?admin=results&jobnumber=%d&pass=%s\n\n" % (self.serverProtocol,self.serverHost,self.jobNumber,self.password))
672
+ f.close()
673
+ except IOError as e:
674
+ self.Error("Could not append to log file %s" % self.logfilename)
675
+
676
+ try:
677
+ f = open(self.statfilename,'a')
678
+ f.write("=1\n\n")
679
+ f.write("\nNEOS job#=%d, pass=%s\n\n" % (self.jobNumber,self.password))
680
+ f.write("Check the following URL for progress report :\n")
681
+ f.write("%s://%s/neos/cgi-bin/nph-neos-solver.cgi?admin=results&jobnumber=%d&pass=%s\n\n" % (self.serverProtocol,self.serverHost,self.jobNumber,self.password))
682
+ f.write("=2\n")
683
+ f.close()
684
+ except IOError as e:
685
+ self.Error("Could not append to status file %s\n" % self.statfilename)
686
+
687
+ def getText(self,node):
688
+ """
689
+ Returns the text from the node of an xml document
690
+ """
691
+ s = ""
692
+ if isinstance(node,str):
693
+ return node
694
+ if isinstance(node.nodeValue,str):
695
+ return node.data
696
+ elif node.hasChildNodes():
697
+ for n in node.childNodes:
698
+ s += self.getText(n)
699
+ return s
700
+
701
+ def parseSolution(self,xmlstring):
702
+ doc = xml.dom.minidom.parseString(xmlstring)
703
+ for tag in ['allsolutions', 'scenrep']:
704
+ xmltag = tag[:4]
705
+ node = doc.getElementsByTagName(xmltag)
706
+ if node and len(node):
707
+ try:
708
+ f = open(os.path.join(self.scrdir, f"{tag}.{self.scrext}"), 'wb')
709
+ f.write(bytes.fromhex(self.getText(node[0])))
710
+ f.close()
711
+ except IOError as e:
712
+ self.Error("Could not write file %s.%s\n" % (tag, self.scrext))
713
+ node = doc.getElementsByTagName('solu')
714
+ if node and len(node):
715
+ try:
716
+ f = open(self.solufilename,'w')
717
+ f.write(self.getText(node[0]))
718
+ f.close()
719
+ except IOError as e:
720
+ self.Error("Could not write solution file %s\n" % self.solufilename)
721
+
722
+ node = doc.getElementsByTagName('stat')
723
+ if node and len(node):
724
+ try:
725
+ f = open(self.statfilename,'w',errors='replace')
726
+ f.write(self.getText(node[0]))
727
+ f.close()
728
+ except IOError as e:
729
+ self.Error("Could not write status file %s\n" % self.statfilename)
730
+
731
+ node = doc.getElementsByTagName('log')
732
+ if node and len(node):
733
+ if self.logopt in [1,3,4]:
734
+ # Send the output to the screen
735
+ encoding = sys.stdout.encoding
736
+ encoded_text = self.getText(node[0]).encode(encoding,errors='replace').decode(encoding)
737
+ sys.stdout.write(encoded_text)
738
+ if self.logopt in [2,4]:
739
+ # Append the error message to the logfile indicated
740
+ try:
741
+ f = open(self.logfilename,'a',errors='replace')
742
+ f.write(self.getText(node[0]))
743
+ f.close()
744
+ except IOError as e:
745
+ self.Error("Could not append log file %s\n" % self.logfilename)
746
+
747
+ doc.unlink()
748
+
749
+ def getResults(self):
750
+ offset = 0
751
+ status = self.neos.getJobStatus(self.jobNumber,self.password)
752
+ try:
753
+ while (status == "Waiting" or status=="Running"):
754
+ (results,offset) = self.neos.getIntermediateResults(self.jobNumber, self.password,offset)
755
+ if isinstance(results,xmlrpc.client.Binary):
756
+ results = results.data.decode()
757
+ if results and len(results):
758
+
759
+ if self.logopt in [1,3,4]:
760
+ # Send the output to the screen
761
+ sys.stdout.write(results)
762
+ if self.logopt in [2,4]:
763
+ # Append the error message to the logfile indicated
764
+ try:
765
+ f = open(self.logfilename,'a')
766
+ f.write(results)
767
+ f.close()
768
+ except IOError as e:
769
+ self.Error("Could not append to log file %s" % self.logfilename)
770
+
771
+ try:
772
+ f = open(self.statfilename,'a')
773
+ f.write("=1\n\n")
774
+ f.write(results)
775
+ f.write("=2\n")
776
+ f.close()
777
+ except IOError as e:
778
+ self.Error("Could not append to status file %s\n" % self.statfilename)
779
+ status = self.neos.getJobStatus(self.jobNumber,self.password)
780
+ time.sleep(5)
781
+
782
+ except KeyboardInterrupt as e:
783
+ msg = '''Keyboard Interrupt\n\
784
+ Job is still running on remote machine\n\
785
+ To retrieve results, run GAMS using solver 'kestrel' with option file:\n\
786
+ kestrel_job %d\n\
787
+ kestrel_pass %s\n\n\
788
+ To stop job, run GAMS using solver 'kestrelkil' with above option file\n\
789
+ ''' % (self.jobNumber, self.password)
790
+ self.Error(msg)
791
+
792
+ resultsXML = self.neos.getFinalResults(self.jobNumber,self.password)
793
+ if isinstance(resultsXML,xmlrpc.client.Binary):
794
+ resultsXML = resultsXML.data
795
+ self.parseSolution(resultsXML)
796
+
797
+ if __name__=="__main__":
798
+ # print 'in gmske_ux.out'
799
+ # Initialization phase
800
+
801
+ try:
802
+ kestrel = KestrelGamsClient(sys.argv)
803
+ kestrel.parseControlFile()
804
+ try:
805
+ f = open(os.path.join(pathlib.Path(__file__).parent.absolute(),'gamsstmp.txt'),'r')
806
+ auditLine = f.readline()
807
+ f.close()
808
+ kestrel.writeLog('NEOS Kestrel ' + auditLine)
809
+ except:
810
+ pass
811
+ kestrel.writeLog('\nFor terms of use please inspect https://neos-server.org/neos/termofuse.html\n\n')
812
+ kestrel.writeErrorOutputFiles()
813
+ kestrel.parseOptionsFile()
814
+ kestrel.connectServer()
815
+ kestrel.obtainSolvers()
816
+ except KestrelException as e:
817
+ kestrel.Error(e.msg)
818
+
819
+ if kestrel.action=="solve":
820
+ # Solve with job number and password retrieves the results
821
+ # Otherwise we obtain them from the submission
822
+
823
+ try:
824
+ kestrel.parseOptionsFile()
825
+ kestrel.writeLog("NEOS Solver: %s\n" % kestrel.solverName)
826
+ if (not kestrel.jobNumber) or (not kestrel.password):
827
+ kestrel.checkOptionsFile()
828
+ kestrel.formSubmission()
829
+ kestrel.submit()
830
+ kestrel.getResults()
831
+ except KestrelException as e:
832
+ kestrel.Error(e.msg)
833
+
834
+ elif kestrel.action=="submit":
835
+ try:
836
+ kestrel.parseOptionsFile()
837
+ kestrel.checkOptionsFile()
838
+ kestrel.formSubmission()
839
+ kestrel.submit()
840
+
841
+ fname = os.path.join(kestrel.scrdir, "kestrel." + kestrel.scrext)
842
+ try:
843
+ f = open(fname,'a')
844
+ f.write("%d %s\n" % (kestrel.jobNumber, kestrel.password))
845
+ f.close()
846
+ except IOError as e:
847
+ kestrel.Error("Could not append to submission file %s\n" % fname)
848
+
849
+ except KestrelException as e:
850
+ kestrel.Error(e.msg)
851
+
852
+ elif kestrel.action=="retrieve":
853
+ fname = os.path.join(kestrel.scrdir, "kestrel." + kestrel.scrext)
854
+
855
+ try:
856
+ f = open(fname,'r')
857
+ except IOError as e:
858
+ kestrel.Error("Could not open submission file %s\n" % fname)
859
+
860
+ m = re.match(r'(\d+) ([a-zA-Z]+)',f.readline())
861
+ if m:
862
+ kestrel.jobNumber = int(m.groups()[0])
863
+ kestrel.password = m.groups()[1]
864
+ rest = f.read()
865
+ f.close()
866
+
867
+ if kestrel.jobNumber and kestrel.password:
868
+ try:
869
+ kestrel.getResults()
870
+ except KestrelException as e:
871
+ kestrel.Error(e.msg)
872
+ else:
873
+ kestrel.Error( "Corrupt submission file %s\n" % fname)
874
+
875
+ if (rest):
876
+ try:
877
+ f = open(fname,'w')
878
+ f.write(rest);
879
+ f.close()
880
+ except IOError as e:
881
+ kestrel.Error("Could not rewrite submission file %s\n" % fname)
882
+ else:
883
+ os.unlink(fname)
884
+
885
+ elif kestrel.action=="kill":
886
+ # Kill and job retrieval do not require a valid solver
887
+ kestrel.parseOptionsFile()
888
+ if kestrel.jobNumber and kestrel.password:
889
+ response = kestrel.neos.killJob(kestrel.jobNumber,kestrel.password)
890
+
891
+ if kestrel.logopt in [1,3,4]:
892
+ # Send the output to the screen
893
+ sys.stdout.write("\n%s\n\n" % response)
894
+ elif (kestrel.logopt == 2):
895
+ # Append the error message to the logfile indicated
896
+ try:
897
+ f = open(kestrel.logfilename,'a')
898
+ f.write("\n%s\n\n" % response)
899
+ f.close()
900
+ except IOError as e:
901
+ kestrel.Error("Could not append to log file %s" % kestrel.logfilename)
902
+
903
+ try:
904
+ f = open(kestrel.statfilename,'a')
905
+ f.write("=1\n\n")
906
+ f.write("%s\n\n" % response)
907
+ f.write("=2\n")
908
+ f.close()
909
+ except IOError as e:
910
+ kestrel.Error("Could not append to status file %s\n" % kestrel.statfilename)
911
+ else:
912
+ kestrel.Error( "No 'kestrel_job' and 'kestrel_pass' options found in %s\n\n" % kestrel.optfilename)