gamsapi 52.5.0__cp312-cp312-win_amd64.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 (257) hide show
  1. gams/__init__.py +27 -0
  2. gams/_version.py +1 -0
  3. gams/connect/__init__.py +28 -0
  4. gams/connect/agents/__init__.py +24 -0
  5. gams/connect/agents/_excel/__init__.py +32 -0
  6. gams/connect/agents/_excel/excelagent.py +312 -0
  7. gams/connect/agents/_excel/workbook.py +155 -0
  8. gams/connect/agents/_sqlconnectors/__init__.py +42 -0
  9. gams/connect/agents/_sqlconnectors/_accesshandler.py +211 -0
  10. gams/connect/agents/_sqlconnectors/_databasehandler.py +250 -0
  11. gams/connect/agents/_sqlconnectors/_mysqlhandler.py +168 -0
  12. gams/connect/agents/_sqlconnectors/_postgreshandler.py +131 -0
  13. gams/connect/agents/_sqlconnectors/_pyodbchandler.py +112 -0
  14. gams/connect/agents/_sqlconnectors/_sqlalchemyhandler.py +74 -0
  15. gams/connect/agents/_sqlconnectors/_sqlitehandler.py +262 -0
  16. gams/connect/agents/_sqlconnectors/_sqlserverhandler.py +179 -0
  17. gams/connect/agents/concatenate.py +440 -0
  18. gams/connect/agents/connectagent.py +743 -0
  19. gams/connect/agents/csvreader.py +675 -0
  20. gams/connect/agents/csvwriter.py +151 -0
  21. gams/connect/agents/domainwriter.py +143 -0
  22. gams/connect/agents/excelreader.py +756 -0
  23. gams/connect/agents/excelwriter.py +467 -0
  24. gams/connect/agents/filter.py +223 -0
  25. gams/connect/agents/gamsreader.py +112 -0
  26. gams/connect/agents/gamswriter.py +239 -0
  27. gams/connect/agents/gdxreader.py +109 -0
  28. gams/connect/agents/gdxwriter.py +146 -0
  29. gams/connect/agents/labelmanipulator.py +303 -0
  30. gams/connect/agents/projection.py +539 -0
  31. gams/connect/agents/pythoncode.py +71 -0
  32. gams/connect/agents/rawcsvreader.py +248 -0
  33. gams/connect/agents/rawexcelreader.py +312 -0
  34. gams/connect/agents/schema/CSVReader.yaml +92 -0
  35. gams/connect/agents/schema/CSVWriter.yaml +44 -0
  36. gams/connect/agents/schema/Concatenate.yaml +52 -0
  37. gams/connect/agents/schema/DomainWriter.yaml +25 -0
  38. gams/connect/agents/schema/ExcelReader.yaml +121 -0
  39. gams/connect/agents/schema/ExcelWriter.yaml +78 -0
  40. gams/connect/agents/schema/Filter.yaml +74 -0
  41. gams/connect/agents/schema/GAMSReader.yaml +20 -0
  42. gams/connect/agents/schema/GAMSWriter.yaml +47 -0
  43. gams/connect/agents/schema/GDXReader.yaml +23 -0
  44. gams/connect/agents/schema/GDXWriter.yaml +32 -0
  45. gams/connect/agents/schema/LabelManipulator.yaml +99 -0
  46. gams/connect/agents/schema/Projection.yaml +24 -0
  47. gams/connect/agents/schema/PythonCode.yaml +6 -0
  48. gams/connect/agents/schema/RawCSVReader.yaml +34 -0
  49. gams/connect/agents/schema/RawExcelReader.yaml +42 -0
  50. gams/connect/agents/schema/SQLReader.yaml +75 -0
  51. gams/connect/agents/schema/SQLWriter.yaml +103 -0
  52. gams/connect/agents/sqlreader.py +301 -0
  53. gams/connect/agents/sqlwriter.py +276 -0
  54. gams/connect/connectdatabase.py +275 -0
  55. gams/connect/connectvalidator.py +93 -0
  56. gams/connect/errors.py +34 -0
  57. gams/control/__init__.py +136 -0
  58. gams/control/database.py +2231 -0
  59. gams/control/execution.py +1900 -0
  60. gams/control/options.py +2792 -0
  61. gams/control/workspace.py +1198 -0
  62. gams/core/__init__.py +24 -0
  63. gams/core/cfg/__init__.py +26 -0
  64. gams/core/cfg/_cfgmcc.cp312-win_amd64.pyd +0 -0
  65. gams/core/cfg/cfgmcc.py +519 -0
  66. gams/core/dct/__init__.py +26 -0
  67. gams/core/dct/_dctmcc.cp312-win_amd64.pyd +0 -0
  68. gams/core/dct/dctmcc.py +574 -0
  69. gams/core/embedded/__init__.py +26 -0
  70. gams/core/embedded/gamsemb.py +1024 -0
  71. gams/core/emp/__init__.py +24 -0
  72. gams/core/emp/emplexer.py +89 -0
  73. gams/core/emp/empyacc.py +281 -0
  74. gams/core/gdx/__init__.py +26 -0
  75. gams/core/gdx/_gdxcc.cp312-win_amd64.pyd +0 -0
  76. gams/core/gdx/gdxcc.py +866 -0
  77. gams/core/gev/__init__.py +26 -0
  78. gams/core/gev/_gevmcc.cp312-win_amd64.pyd +0 -0
  79. gams/core/gev/gevmcc.py +855 -0
  80. gams/core/gmd/__init__.py +26 -0
  81. gams/core/gmd/_gmdcc.cp312-win_amd64.pyd +0 -0
  82. gams/core/gmd/gmdcc.py +917 -0
  83. gams/core/gmo/__init__.py +26 -0
  84. gams/core/gmo/_gmomcc.cp312-win_amd64.pyd +0 -0
  85. gams/core/gmo/gmomcc.py +2046 -0
  86. gams/core/idx/__init__.py +26 -0
  87. gams/core/idx/_idxcc.cp312-win_amd64.pyd +0 -0
  88. gams/core/idx/idxcc.py +510 -0
  89. gams/core/numpy/__init__.py +29 -0
  90. gams/core/numpy/_gams2numpy.cp312-win_amd64.pyd +0 -0
  91. gams/core/numpy/gams2numpy.py +1048 -0
  92. gams/core/opt/__init__.py +26 -0
  93. gams/core/opt/_optcc.cp312-win_amd64.pyd +0 -0
  94. gams/core/opt/optcc.py +840 -0
  95. gams/engine/__init__.py +204 -0
  96. gams/engine/api/__init__.py +13 -0
  97. gams/engine/api/auth_api.py +7653 -0
  98. gams/engine/api/cleanup_api.py +751 -0
  99. gams/engine/api/default_api.py +887 -0
  100. gams/engine/api/hypercube_api.py +2629 -0
  101. gams/engine/api/jobs_api.py +5229 -0
  102. gams/engine/api/licenses_api.py +2220 -0
  103. gams/engine/api/namespaces_api.py +7783 -0
  104. gams/engine/api/usage_api.py +5627 -0
  105. gams/engine/api/users_api.py +5931 -0
  106. gams/engine/api_client.py +804 -0
  107. gams/engine/api_response.py +21 -0
  108. gams/engine/configuration.py +601 -0
  109. gams/engine/exceptions.py +216 -0
  110. gams/engine/models/__init__.py +86 -0
  111. gams/engine/models/bad_input.py +89 -0
  112. gams/engine/models/cleanable_job_result.py +104 -0
  113. gams/engine/models/cleanable_job_result_page.py +113 -0
  114. gams/engine/models/engine_license.py +107 -0
  115. gams/engine/models/files_not_found.py +93 -0
  116. gams/engine/models/forwarded_token_response.py +112 -0
  117. gams/engine/models/generic_key_value_pair.py +89 -0
  118. gams/engine/models/hypercube.py +160 -0
  119. gams/engine/models/hypercube_page.py +111 -0
  120. gams/engine/models/hypercube_summary.py +91 -0
  121. gams/engine/models/hypercube_token.py +97 -0
  122. gams/engine/models/identity_provider.py +107 -0
  123. gams/engine/models/identity_provider_ldap.py +121 -0
  124. gams/engine/models/identity_provider_oauth2.py +146 -0
  125. gams/engine/models/identity_provider_oauth2_scope.py +89 -0
  126. gams/engine/models/identity_provider_oauth2_with_secret.py +152 -0
  127. gams/engine/models/identity_provider_oidc.py +133 -0
  128. gams/engine/models/identity_provider_oidc_with_secret.py +143 -0
  129. gams/engine/models/inex.py +91 -0
  130. gams/engine/models/invitation.py +136 -0
  131. gams/engine/models/invitation_quota.py +106 -0
  132. gams/engine/models/invitation_token.py +87 -0
  133. gams/engine/models/job.py +165 -0
  134. gams/engine/models/job_no_text_entry.py +138 -0
  135. gams/engine/models/job_no_text_entry_page.py +111 -0
  136. gams/engine/models/license.py +91 -0
  137. gams/engine/models/log_piece.py +96 -0
  138. gams/engine/models/message.py +87 -0
  139. gams/engine/models/message_and_token.py +99 -0
  140. gams/engine/models/message_with_webhook_id.py +89 -0
  141. gams/engine/models/model_auth_token.py +87 -0
  142. gams/engine/models/model_configuration.py +125 -0
  143. gams/engine/models/model_default_instance.py +99 -0
  144. gams/engine/models/model_default_user_instance.py +98 -0
  145. gams/engine/models/model_hypercube_job.py +106 -0
  146. gams/engine/models/model_hypercube_usage.py +130 -0
  147. gams/engine/models/model_instance_info.py +116 -0
  148. gams/engine/models/model_instance_info_full.py +123 -0
  149. gams/engine/models/model_instance_pool_info.py +112 -0
  150. gams/engine/models/model_job_labels.py +179 -0
  151. gams/engine/models/model_job_usage.py +133 -0
  152. gams/engine/models/model_pool_usage.py +124 -0
  153. gams/engine/models/model_usage.py +115 -0
  154. gams/engine/models/model_user.py +96 -0
  155. gams/engine/models/model_userinstance_info.py +119 -0
  156. gams/engine/models/model_userinstancepool_info.py +95 -0
  157. gams/engine/models/model_version.py +91 -0
  158. gams/engine/models/models.py +120 -0
  159. gams/engine/models/namespace.py +104 -0
  160. gams/engine/models/namespace_quota.py +96 -0
  161. gams/engine/models/namespace_with_permission.py +96 -0
  162. gams/engine/models/not_found.py +91 -0
  163. gams/engine/models/password_policy.py +97 -0
  164. gams/engine/models/perm_and_username.py +89 -0
  165. gams/engine/models/quota.py +117 -0
  166. gams/engine/models/quota_exceeded.py +97 -0
  167. gams/engine/models/status_code_meaning.py +89 -0
  168. gams/engine/models/stream_entry.py +89 -0
  169. gams/engine/models/system_wide_license.py +92 -0
  170. gams/engine/models/text_entries.py +87 -0
  171. gams/engine/models/text_entry.py +101 -0
  172. gams/engine/models/time_span.py +95 -0
  173. gams/engine/models/time_span_pool_worker.py +99 -0
  174. gams/engine/models/token_forward_error.py +87 -0
  175. gams/engine/models/user.py +127 -0
  176. gams/engine/models/user_group_member.py +96 -0
  177. gams/engine/models/user_groups.py +108 -0
  178. gams/engine/models/vapid_info.py +87 -0
  179. gams/engine/models/webhook.py +138 -0
  180. gams/engine/models/webhook_parameterized_event.py +99 -0
  181. gams/engine/py.typed +0 -0
  182. gams/engine/rest.py +258 -0
  183. gams/magic/__init__.py +32 -0
  184. gams/magic/gams_magic.py +142 -0
  185. gams/magic/interactive.py +402 -0
  186. gams/tools/__init__.py +30 -0
  187. gams/tools/errors.py +34 -0
  188. gams/tools/toolcollection/__init__.py +24 -0
  189. gams/tools/toolcollection/alg/__init__.py +24 -0
  190. gams/tools/toolcollection/alg/rank.py +51 -0
  191. gams/tools/toolcollection/data/__init__.py +24 -0
  192. gams/tools/toolcollection/data/csvread.py +444 -0
  193. gams/tools/toolcollection/data/csvwrite.py +311 -0
  194. gams/tools/toolcollection/data/exceldump.py +47 -0
  195. gams/tools/toolcollection/data/sqlitewrite.py +276 -0
  196. gams/tools/toolcollection/gdxservice/__init__.py +24 -0
  197. gams/tools/toolcollection/gdxservice/gdxencoding.py +104 -0
  198. gams/tools/toolcollection/gdxservice/gdxrename.py +94 -0
  199. gams/tools/toolcollection/linalg/__init__.py +24 -0
  200. gams/tools/toolcollection/linalg/cholesky.py +57 -0
  201. gams/tools/toolcollection/linalg/eigenvalue.py +56 -0
  202. gams/tools/toolcollection/linalg/eigenvector.py +58 -0
  203. gams/tools/toolcollection/linalg/invert.py +55 -0
  204. gams/tools/toolcollection/linalg/ols.py +138 -0
  205. gams/tools/toolcollection/tooltemplate.py +321 -0
  206. gams/tools/toolcollection/win32/__init__.py +24 -0
  207. gams/tools/toolcollection/win32/excelmerge.py +93 -0
  208. gams/tools/toolcollection/win32/exceltalk.py +76 -0
  209. gams/tools/toolcollection/win32/msappavail.py +49 -0
  210. gams/tools/toolcollection/win32/shellexecute.py +54 -0
  211. gams/tools/tools.py +116 -0
  212. gams/transfer/__init__.py +35 -0
  213. gams/transfer/_abcs/__init__.py +37 -0
  214. gams/transfer/_abcs/container_abcs.py +433 -0
  215. gams/transfer/_internals/__init__.py +63 -0
  216. gams/transfer/_internals/algorithms.py +436 -0
  217. gams/transfer/_internals/casepreservingdict.py +124 -0
  218. gams/transfer/_internals/constants.py +270 -0
  219. gams/transfer/_internals/domainviolation.py +103 -0
  220. gams/transfer/_internals/specialvalues.py +172 -0
  221. gams/transfer/containers/__init__.py +26 -0
  222. gams/transfer/containers/_container.py +1794 -0
  223. gams/transfer/containers/_io/__init__.py +28 -0
  224. gams/transfer/containers/_io/containers.py +164 -0
  225. gams/transfer/containers/_io/gdx.py +1029 -0
  226. gams/transfer/containers/_io/gmd.py +872 -0
  227. gams/transfer/containers/_mixins/__init__.py +26 -0
  228. gams/transfer/containers/_mixins/ccc.py +1274 -0
  229. gams/transfer/syms/__init__.py +33 -0
  230. gams/transfer/syms/_methods/__init__.py +24 -0
  231. gams/transfer/syms/_methods/tables.py +120 -0
  232. gams/transfer/syms/_methods/toDict.py +115 -0
  233. gams/transfer/syms/_methods/toList.py +83 -0
  234. gams/transfer/syms/_methods/toValue.py +60 -0
  235. gams/transfer/syms/_mixins/__init__.py +32 -0
  236. gams/transfer/syms/_mixins/equals.py +626 -0
  237. gams/transfer/syms/_mixins/generateRecords.py +499 -0
  238. gams/transfer/syms/_mixins/pivot.py +313 -0
  239. gams/transfer/syms/_mixins/pve.py +627 -0
  240. gams/transfer/syms/_mixins/sa.py +27 -0
  241. gams/transfer/syms/_mixins/sapve.py +27 -0
  242. gams/transfer/syms/_mixins/saua.py +27 -0
  243. gams/transfer/syms/_mixins/sauapve.py +199 -0
  244. gams/transfer/syms/_mixins/spve.py +1528 -0
  245. gams/transfer/syms/_mixins/ve.py +936 -0
  246. gams/transfer/syms/container_syms/__init__.py +31 -0
  247. gams/transfer/syms/container_syms/_alias.py +984 -0
  248. gams/transfer/syms/container_syms/_equation.py +333 -0
  249. gams/transfer/syms/container_syms/_parameter.py +973 -0
  250. gams/transfer/syms/container_syms/_set.py +604 -0
  251. gams/transfer/syms/container_syms/_universe_alias.py +461 -0
  252. gams/transfer/syms/container_syms/_variable.py +321 -0
  253. gamsapi-52.5.0.dist-info/METADATA +150 -0
  254. gamsapi-52.5.0.dist-info/RECORD +257 -0
  255. gamsapi-52.5.0.dist-info/WHEEL +5 -0
  256. gamsapi-52.5.0.dist-info/licenses/LICENSE +22 -0
  257. gamsapi-52.5.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,1024 @@
1
+ #
2
+ # GAMS - General Algebraic Modeling System Python API
3
+ #
4
+ # Copyright (c) 2017-2026 GAMS Development Corp. <support@gams.com>
5
+ # Copyright (c) 2017-2026 GAMS Software GmbH <support@gams.com>
6
+ #
7
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ # of this software and associated documentation files (the "Software"), to deal
9
+ # in the Software without restriction, including without limitation the rights
10
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ # copies of the Software, and to permit persons to whom the Software is
12
+ # furnished to do so, subject to the following conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be included in all
15
+ # copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
+ # SOFTWARE.
24
+ #
25
+
26
+ from gams.core.gmd import *
27
+ import sys
28
+ import traceback
29
+ from gams import *
30
+ from gams.control.database import _GamsSymbol
31
+
32
+
33
+ def _intValueAndFree(intP):
34
+ intp_val = intp_value(intP)
35
+ delete_intp(intP)
36
+ return intp_val
37
+
38
+
39
+ class KeyFormat(object):
40
+ TUPLE = "tuple"
41
+ FLAT = "flat"
42
+ SKIP = "skip"
43
+ AUTO = "auto"
44
+
45
+
46
+ class ValueFormat(object):
47
+ TUPLE = "tuple"
48
+ FLAT = "flat"
49
+ SKIP = "skip"
50
+ AUTO = "auto"
51
+
52
+
53
+ class RecordFormat(object):
54
+ TUPLE = "tuple"
55
+ FLAT = "flat"
56
+ AUTO = "auto"
57
+
58
+
59
+ class KeyType(object):
60
+ STRING = "string"
61
+ INT = "int"
62
+
63
+
64
+ class MergeType(object):
65
+ REPLACE = "replace"
66
+ MERGE = "merge"
67
+ DEFAULT = "default"
68
+
69
+
70
+ class DomainCheckType(object):
71
+ FILTERED = "filtered"
72
+ CHECKED = "checked"
73
+ DEFAULT = "default"
74
+
75
+
76
+ def merge_type_to_int(mergeType):
77
+ if mergeType == MergeType.REPLACE:
78
+ return 0
79
+ if mergeType == MergeType.MERGE:
80
+ return 1
81
+ if mergeType == MergeType.DEFAULT:
82
+ return 2
83
+ raise Exception("Unexpected argument " + mergeType + " to merge_type_to_int")
84
+
85
+
86
+ def domcheck_type_to_int(domCheck):
87
+ if domCheck == DomainCheckType.FILTERED:
88
+ return 0
89
+ if domCheck == DomainCheckType.CHECKED:
90
+ return 1
91
+ if domCheck == DomainCheckType.DEFAULT:
92
+ return 2
93
+ raise Exception("Unexpected argument " + domCheck + " to domcheck_type_to_int")
94
+
95
+
96
+ class ECSymbol(object):
97
+
98
+ def __init__(
99
+ self,
100
+ ecDB,
101
+ symPtr,
102
+ keyType,
103
+ keyFormat,
104
+ valueFormat,
105
+ recordFormat,
106
+ mergeType=MergeType.DEFAULT,
107
+ domCheck=DomainCheckType.DEFAULT,
108
+ ):
109
+ self._ecDB = ecDB
110
+ self._symPtr = symPtr
111
+ self._keyType = keyType.lower()
112
+ self._keyFormat = keyFormat.lower()
113
+ self._valueFormat = valueFormat.lower()
114
+ self._recordFormat = recordFormat.lower()
115
+ self._mergeType = mergeType.lower()
116
+ self._domCheck = domCheck
117
+
118
+ self._symIterPtr = None
119
+
120
+ ret = gmdSymbolInfo(self._ecDB._gmd, self._symPtr, GMD_DIM)
121
+ self._ecDB._check_for_gmd_error(ret[0])
122
+ self._dim = ret[1]
123
+
124
+ rc, type = gmdSymbolType(self._ecDB._gmd, self._symPtr)
125
+ self._ecDB._check_for_gmd_error(rc)
126
+ self._type = type
127
+
128
+ if self._type == dt_par:
129
+ self._values = self._valPar
130
+ elif self._type in [dt_set, dt_alias]:
131
+ self._values = self._valSet
132
+ elif self._type in [dt_var, dt_equ]:
133
+ self._values = self._valVarEqu
134
+
135
+ self._setDefaultFormat()
136
+
137
+ def __del__(self):
138
+ if self._symIterPtr:
139
+ rc = gmdFreeSymbolIterator(self._ecDB._gmd, self._symIterPtr)
140
+ self._ecDB._check_for_gmd_error(rc)
141
+ self._symIterPtr = None
142
+
143
+ def __len__(self):
144
+ ret = gmdSymbolInfo(self._ecDB._gmd, self._symPtr, GMD_NRRECORDS)
145
+ self._ecDB._check_for_gmd_error(ret[0])
146
+ return ret[1]
147
+
148
+ def getSymbolNumber(self):
149
+ ret = gmdSymbolInfo(self._ecDB._gmd, self._symPtr, GMD_NUMBER)
150
+ self._ecDB._check_for_gmd_error(ret[0])
151
+ return ret[1]
152
+
153
+ def _clear(self):
154
+ gmdClearSymbol(self._ecDB._gmd, self._symPtr)
155
+
156
+ def _writeSet(self, parseKeys, parseValues, data, keyType):
157
+ self._clear()
158
+ if keyType == KeyType.STRING:
159
+ for rec in data:
160
+ rc = new_intp()
161
+ symIterPtr = gmdAddRecordPy(
162
+ self._ecDB._gmd, self._symPtr, parseKeys(rec), rc
163
+ )
164
+ self._ecDB._check_for_gmd_error(_intValueAndFree(rc))
165
+ rc = gmdSetElemText(self._ecDB._gmd, symIterPtr, parseValues(rec))
166
+ self._ecDB._check_for_gmd_error(rc)
167
+ rc = gmdFreeSymbolIterator(self._ecDB._gmd, symIterPtr)
168
+ self._ecDB._check_for_gmd_error(rc)
169
+ elif keyType == KeyType.INT:
170
+ keyArray = intArray(
171
+ self._dim
172
+ ) # TODO: move key generation into lambda function
173
+ valueArray = doubleArray(1)
174
+ if parseValues(next(iter(data))) != "":
175
+ valueArray[0] = 1
176
+ else:
177
+ valueArray[0] = 0
178
+ for rec in data:
179
+ keyList = parseKeys(rec)
180
+ for idx in range(self._dim):
181
+ keyArray[idx] = keyList[idx]
182
+ rc = gmdAddRecordRaw(
183
+ self._ecDB._gmd,
184
+ self._symPtr,
185
+ keyArray,
186
+ valueArray,
187
+ parseValues(rec),
188
+ )
189
+ self._ecDB._check_for_gmd_error(rc)
190
+
191
+ def _writeParameter(self, parseKeys, parseValues, data, keyType):
192
+ self._clear()
193
+ if keyType == KeyType.STRING:
194
+ for rec in data:
195
+ rc = new_intp()
196
+ symIterPtr = gmdAddRecordPy(
197
+ self._ecDB._gmd, self._symPtr, parseKeys(rec), rc
198
+ )
199
+ self._ecDB._check_for_gmd_error(_intValueAndFree(rc))
200
+ rc = gmdSetLevel(self._ecDB._gmd, symIterPtr, parseValues(rec))
201
+ self._ecDB._check_for_gmd_error(rc)
202
+ rc = gmdFreeSymbolIterator(self._ecDB._gmd, symIterPtr)
203
+ self._ecDB._check_for_gmd_error(rc)
204
+ elif keyType == KeyType.INT:
205
+ keyArray = intArray(
206
+ self._dim
207
+ ) # TODO: move key generation into lambda function
208
+ valueArray = doubleArray(1)
209
+ for rec in data:
210
+ rc = new_intp()
211
+ keyList = parseKeys(rec)
212
+ for idx in range(self._dim):
213
+ keyArray[idx] = keyList[idx]
214
+ valueArray[0] = parseValues(rec)
215
+ rc = gmdAddRecordRaw(
216
+ self._ecDB._gmd, self._symPtr, keyArray, valueArray, ""
217
+ )
218
+ self._ecDB._check_for_gmd_error(rc)
219
+
220
+ # TODO: combine with _writePar for reduced redundancy
221
+ def _writeVarEqu(self, parseKeys, parseValues, data, keyType):
222
+ self._clear()
223
+ if keyType == KeyType.STRING:
224
+ for rec in data:
225
+ rc = new_intp()
226
+ symIterPtr = gmdAddRecordPy(
227
+ self._ecDB._gmd, self._symPtr, parseKeys(rec), rc
228
+ )
229
+ self._ecDB._check_for_gmd_error(_intValueAndFree(rc))
230
+ values = parseValues(rec)
231
+ values = list(map(lambda x: x, values))
232
+ rc = gmdSetLevel(self._ecDB._gmd, symIterPtr, values[0])
233
+ self._ecDB._check_for_gmd_error(rc)
234
+ rc = gmdSetMarginal(self._ecDB._gmd, symIterPtr, values[1])
235
+ self._ecDB._check_for_gmd_error(rc)
236
+ rc = gmdSetLower(self._ecDB._gmd, symIterPtr, values[2])
237
+ self._ecDB._check_for_gmd_error(rc)
238
+ rc = gmdSetUpper(self._ecDB._gmd, symIterPtr, values[3])
239
+ self._ecDB._check_for_gmd_error(rc)
240
+ rc = gmdSetScale(self._ecDB._gmd, symIterPtr, values[4])
241
+ self._ecDB._check_for_gmd_error(rc)
242
+ rc = gmdFreeSymbolIterator(self._ecDB._gmd, symIterPtr)
243
+ self._ecDB._check_for_gmd_error(rc)
244
+ elif keyType == KeyType.INT:
245
+ keyArray = intArray(
246
+ self._dim
247
+ ) # TODO: move key generation into lambda function
248
+ valueArray = doubleArray(5)
249
+ for rec in data:
250
+ keyList = parseKeys(rec)
251
+ valueList = parseValues(rec)
252
+ for idx in range(self._dim):
253
+ keyArray[idx] = keyList[idx]
254
+ for idx in range(5):
255
+ valueArray[idx] = valueList[idx]
256
+ rc = gmdAddRecordRaw(
257
+ self._ecDB._gmd, self._symPtr, keyArray, valueArray, ""
258
+ )
259
+ self._ecDB._check_for_gmd_error(rc)
260
+
261
+ def _name(self):
262
+ ret = gmdSymbolInfo(self._ecDB._gmd, self._symPtr, GMD_NAME)
263
+ self._ecDB._check_for_gmd_error(ret[0])
264
+ return ret[3]
265
+
266
+ def _setDefaultFormat(self):
267
+ # set defaults if AUTO is specified
268
+ if self._keyFormat == KeyFormat.AUTO:
269
+ if self._dim == 0:
270
+ self._keyFormat = KeyFormat.SKIP
271
+ elif self._dim == 1:
272
+ self._keyFormat = KeyFormat.FLAT
273
+ else:
274
+ self._keyFormat = KeyFormat.TUPLE
275
+ # set defaults if AUTO is specified
276
+ if self._valueFormat == ValueFormat.AUTO:
277
+ if self._type in [dt_set, dt_alias]:
278
+ self._valueFormat = ValueFormat.SKIP
279
+ elif self._type == dt_par:
280
+ self._valueFormat = ValueFormat.FLAT
281
+ else:
282
+ self._valueFormat = ValueFormat.TUPLE
283
+
284
+ # determine possible record format
285
+ rc = new_intp()
286
+ self._symIterPtr = gmdFindFirstRecordPy(self._ecDB._gmd, self._symPtr, rc)
287
+ delete_intp(rc)
288
+ if self._symIterPtr:
289
+ rec = self._getCurrentRecord()
290
+ if len(rec) == 1:
291
+ tmpRecordFormat = RecordFormat.FLAT
292
+ else:
293
+ tmpRecordFormat = RecordFormat.TUPLE
294
+ rc = gmdFreeSymbolIterator(self._ecDB._gmd, self._symIterPtr)
295
+ self._ecDB._check_for_gmd_error(rc)
296
+ else:
297
+ tmpRecordFormat = RecordFormat.FLAT
298
+ self._symIterPtr = None
299
+
300
+ if self._recordFormat == RecordFormat.AUTO:
301
+ self._recordFormat = tmpRecordFormat
302
+ # throw exception if user wants recordFormat.FLAT but we did apply recordFormat.TUPLE. This is the only invalid setting since TUPLE can not be changed to FLAT
303
+ elif (
304
+ self._recordFormat == RecordFormat.FLAT
305
+ and tmpRecordFormat == RecordFormat.TUPLE
306
+ ):
307
+ raise Exception("Can not apply RecordFormat.FLAT")
308
+
309
+ def __getitem__(self, item):
310
+ rc = new_intp()
311
+ if type(item) is str:
312
+ symIterPtr = gmdFindRecordPy(self._ecDB._gmd, self._symPtr, [item], rc)
313
+ elif type(item) is int:
314
+ pass
315
+ # TODO: implement
316
+ elif isinstance(item, tuple):
317
+ symIterPtr = gmdFindRecordPy(self._ecDB._gmd, self._symPtr, list(item), rc)
318
+ self._ecDB._check_for_gmd_error(_intValueAndFree(rc))
319
+ val = self._values(symIterPtr)
320
+ if symIterPtr:
321
+ rc = gmdFreeSymbolIterator(self._ecDB._gmd, symIterPtr)
322
+ self._ecDB._check_for_gmd_error(rc)
323
+ if len(val) == 1:
324
+ return val[0]
325
+
326
+ def __iter__(self):
327
+ self._symIterPtr = None
328
+ return self
329
+
330
+ def __next__(self):
331
+ return self.next()
332
+
333
+ def next(self):
334
+ if self._symIterPtr == None:
335
+ rc = new_intp()
336
+ self._symIterPtr = gmdFindFirstRecordPy(self._ecDB._gmd, self._symPtr, rc)
337
+ delete_intp(rc)
338
+ if self._symIterPtr == None:
339
+ raise StopIteration
340
+ else:
341
+ if not gmdRecordMoveNext(self._ecDB._gmd, self._symIterPtr):
342
+ raise StopIteration
343
+
344
+ rec = self._getCurrentRecord()
345
+ if self._recordFormat == RecordFormat.FLAT:
346
+ return rec[0]
347
+ else:
348
+ return tuple(rec)
349
+
350
+ def _keys(self, symIterPtr):
351
+ if self._keyType == KeyType.STRING:
352
+ rc, keys = gmdGetKeys(self._ecDB._gmd, symIterPtr, self._dim)
353
+ elif self._keyType == KeyType.INT:
354
+ rc, keys, values = gmdGetRecordRaw(self._ecDB._gmd, symIterPtr, self._dim)
355
+ self._ecDB._check_for_gmd_error(rc)
356
+ return tuple(keys)
357
+
358
+ def _values(self, symIterPtr):
359
+ raise Exception(
360
+ "Don't call this method directly, but the implementations for the specific symbol type"
361
+ )
362
+
363
+ def _valSet(self, symIterPtr):
364
+ rc, text = gmdGetElemText(self._ecDB._gmd, symIterPtr)
365
+ self._ecDB._check_for_gmd_error(rc)
366
+ return (text,)
367
+
368
+ def _valPar(self, symIterPtr):
369
+ rc, value = gmdGetLevel(self._ecDB._gmd, symIterPtr)
370
+ if not rc:
371
+ value = float("nan")
372
+ if self._ecDB.epsAsZero:
373
+ value = self._ecDB._mapEPS(value)
374
+ return (value,)
375
+
376
+ def _valVarEqu(self, symIterPtr):
377
+ rc, level = gmdGetLevel(self._ecDB._gmd, symIterPtr)
378
+ if not rc:
379
+ level = float("nan")
380
+ rc, marginal = gmdGetMarginal(self._ecDB._gmd, symIterPtr)
381
+ if not rc:
382
+ marginal = float("nan")
383
+ rc, lower = gmdGetLower(self._ecDB._gmd, symIterPtr)
384
+ if not rc:
385
+ lower = float("nan")
386
+ rc, upper = gmdGetUpper(self._ecDB._gmd, symIterPtr)
387
+ if not rc:
388
+ upper = float("nan")
389
+ rc, scale = gmdGetScale(self._ecDB._gmd, symIterPtr)
390
+ if not rc:
391
+ scale = float("nan")
392
+ if self._ecDB.epsAsZero:
393
+ return tuple(
394
+ map(
395
+ lambda x: self._ecDB._mapEPS(x),
396
+ [level, marginal, lower, upper, scale],
397
+ )
398
+ )
399
+ else:
400
+ return tuple(map(lambda x: x, [level, marginal, lower, upper, scale]))
401
+
402
+ def _getCurrentRecord(self):
403
+ rec = []
404
+
405
+ if self._keyFormat == KeyFormat.TUPLE:
406
+ rec.append(self._keys(self._symIterPtr))
407
+ elif self._keyFormat == KeyFormat.FLAT:
408
+ rec.extend(list(self._keys(self._symIterPtr)))
409
+ elif self._keyFormat == KeyFormat.SKIP:
410
+ pass
411
+
412
+ if self._valueFormat == ValueFormat.TUPLE:
413
+ rec.append(self._values(self._symIterPtr))
414
+ elif self._valueFormat == ValueFormat.FLAT:
415
+ rec.extend(list(self._values(self._symIterPtr)))
416
+ elif self._valueFormat == ValueFormat.SKIP:
417
+ pass
418
+
419
+ return rec
420
+
421
+
422
+ class ECGAMSDatabase(object):
423
+
424
+ def __init__(self, system_directory, gdx_file_name=None):
425
+ gmdGetReady(GMS_SSSIZE)
426
+ self._system_directory = system_directory
427
+ self._gmd = new_gmdHandle_tp()
428
+ self._gmdud = new_gmdHandle_tp()
429
+ self._modSymList = {}
430
+ self._rc = 0
431
+ self._eMsg = ""
432
+ self.arguments = ""
433
+ self._debug = DebugLevel.Off
434
+ self._eps = 4.94066e-324 # copied from value in C#
435
+ self.epsAsZero = True
436
+ self._wsWorkingDir = None
437
+ self._printLog = None
438
+ self._capsule_EMBCODE_DATA = None
439
+ self._shellcode = 0 # error code
440
+
441
+ self._ws = None
442
+ self._db = None
443
+ self._cdb = None
444
+
445
+ # create a dummy GamsWorkspace to check if system_directory is valid
446
+ GamsWorkspace(system_directory=system_directory, debug=self._debug)
447
+
448
+ if gdx_file_name:
449
+ ret = gmdCreate(self._gmd, GMS_SSSIZE)
450
+ if not ret[0]:
451
+ raise Exception(ret[1])
452
+ rc = gmdInitFromGDX(self._gmd, gdx_file_name)
453
+ self._check_for_gmd_error(rc)
454
+
455
+ def _setSpecialValue(self):
456
+ tmp_spec_values = doubleArray(5)
457
+ tmp_spec_values[0] = GMS_SV_UNDEF
458
+ tmp_spec_values[1] = float("nan")
459
+ tmp_spec_values[2] = float("inf")
460
+ tmp_spec_values[3] = float("-inf")
461
+ tmp_spec_values[4] = self._eps
462
+ rc = gmdSetSpecialValues(self._gmd, tmp_spec_values)
463
+ self._check_for_gmd_error(rc)
464
+
465
+ def _setWsWorkingDir(self, val):
466
+ if self._ws:
467
+ self.printLog(
468
+ "Warning: GamsWorkspace was already created. Setting wsWorkingDir has no effect."
469
+ )
470
+ else:
471
+ self._wsWorkingDir = val
472
+
473
+ def _getWsWorkingDir(self):
474
+ if self._ws:
475
+ return self._ws.working_directory
476
+ else:
477
+ return self._wsWorkingDir
478
+
479
+ wsWorkingDir = property(_getWsWorkingDir, _setWsWorkingDir)
480
+
481
+ def _setDebug(self, val):
482
+ if self._ws:
483
+ self.printLog(
484
+ "Warning: GamsWorkspace was already created. Setting debug has no effect."
485
+ )
486
+ else:
487
+ self._debug = val
488
+
489
+ debug = property(None, _setDebug)
490
+
491
+ def _getWS(self):
492
+ if not self._ws:
493
+ self._ws = GamsWorkspace(
494
+ self._wsWorkingDir, self._system_directory, debug=self._debug
495
+ )
496
+ return self._ws
497
+
498
+ ws = property(_getWS)
499
+
500
+ def _getDB(self):
501
+ if not self._db:
502
+ self._db = self._getWS()._add_database_from_gmd(self._gmd)
503
+ if self._getWS().my_eps != None:
504
+ tmp_spec_values = doubleArray(GMS_SVIDX_MAX)
505
+ rc = gmdGetUserSpecialValues(self._gmd, tmp_spec_values)
506
+ self._check_for_gmd_error(rc)
507
+ tmp_spec_values[GMS_SVIDX_EPS] = self._getWS().my_eps
508
+ rc = gmdSetSpecialValues(self._gmd, tmp_spec_values)
509
+ self._check_for_gmd_error(rc)
510
+ return self._db
511
+
512
+ db = property(_getDB)
513
+
514
+ def _mapEPS(self, val):
515
+ if val == self._eps:
516
+ return 0
517
+ return val
518
+
519
+ def _printStdOut(self, msg):
520
+ print(msg)
521
+
522
+ def printLog(self, msg, end="\n"):
523
+ if self._printLog and self._capsule_EMBCODE_DATA != None:
524
+ rc = self._printLog(self._capsule_EMBCODE_DATA, str(msg), end)
525
+ if not rc:
526
+ raise Exception()
527
+ else:
528
+ print("*** " + msg, end=end)
529
+ sys.stdout.flush()
530
+
531
+ def _print_traceback_to_log(self, exception, line_offset=0):
532
+ for frame in traceback.extract_tb(exception.__traceback__):
533
+ self.printLog(
534
+ f' File "{frame.filename}", line {frame.lineno - line_offset}, in {frame.name}'
535
+ )
536
+ if frame.line:
537
+ self.printLog(f" {frame.line}")
538
+
539
+ def print_exception_to_log(self, exception, msg, traceback=True, line_offset=0):
540
+ if traceback:
541
+ self._print_traceback_to_log(exception, line_offset)
542
+ self.printLog(msg)
543
+ sys.stdout.flush()
544
+ sys.stderr.flush()
545
+
546
+ def get_env(self, name):
547
+ import os
548
+
549
+ if os.name == "posix":
550
+ from ctypes import CDLL, c_char_p
551
+
552
+ getenv = CDLL(None).getenv
553
+ getenv.restype = c_char_p
554
+ r = getenv(name.encode())
555
+ try:
556
+ return r.decode()
557
+ except:
558
+ return None
559
+ elif os.name == "nt":
560
+ from ctypes import windll, create_unicode_buffer
561
+
562
+ n = windll.kernel32.GetEnvironmentVariableW(name, None, 0)
563
+ if n == 0:
564
+ return None
565
+ buf = create_unicode_buffer("\0" * n)
566
+ windll.kernel32.GetEnvironmentVariableW(name, buf, n)
567
+ return buf.value
568
+ else:
569
+ raise Exception(f"Unhandled case for os.name={os.name}")
570
+
571
+ def _check_for_gmd_error(self, rc, gmd=None):
572
+ if not rc:
573
+ if gmd is None:
574
+ gmd = self._gmd
575
+ msg = gmdGetLastError(gmd)[1]
576
+ raise Exception(msg)
577
+
578
+ def getUel(self, idx):
579
+ rc, label = gmdGetUelByIndex(self._gmd, idx)
580
+ self._check_for_gmd_error(rc)
581
+ return label
582
+
583
+ def mergeUel(self, label):
584
+ rc, idx = gmdMergeUel(self._gmd, label)
585
+ self._check_for_gmd_error(rc)
586
+ return idx
587
+
588
+ def getUelCount(self):
589
+ ret = gmdInfo(self._gmd, GMD_NRUELS)
590
+ self._check_for_gmd_error(ret[0])
591
+ return ret[1]
592
+
593
+ def get(
594
+ self,
595
+ symbolName,
596
+ keyType=KeyType.STRING,
597
+ keyFormat=KeyFormat.AUTO,
598
+ valueFormat=ValueFormat.AUTO,
599
+ recordFormat=RecordFormat.AUTO,
600
+ ):
601
+ rc = new_intp()
602
+ symPtr = gmdFindSymbolPy(self._gmd, symbolName, rc)
603
+ self._check_for_gmd_error(_intValueAndFree(rc))
604
+ return ECSymbol(self, symPtr, keyType, keyFormat, valueFormat, recordFormat)
605
+
606
+ def _setSet(self, ecSymbol, data):
607
+ firstRec = next(iter(data))
608
+ keyType = KeyType.STRING
609
+ parseValues = lambda rec: ""
610
+
611
+ if not isinstance(firstRec, tuple):
612
+ if ecSymbol._dim != 1:
613
+ raise Exception(
614
+ "Error writing set '"
615
+ + ecSymbol._name()
616
+ + "': Each record needs to be represented as a tuple"
617
+ )
618
+ parseKeys = lambda rec: [rec]
619
+ parseValues = lambda rec: ""
620
+ elif isinstance(firstRec, tuple):
621
+ if isinstance(firstRec[0], tuple):
622
+ parseKeysX = lambda rec: list(rec[0])
623
+ valueIdx = 1
624
+ else:
625
+ parseKeysX = lambda rec: list(rec[0 : ecSymbol._dim])
626
+ valueIdx = ecSymbol._dim
627
+ parseKeys = lambda rec: [ecSymbol._mapKeys(r) for r in parseKeysX(rec)]
628
+
629
+ if len(firstRec) > valueIdx:
630
+ if isinstance(firstRec[valueIdx], tuple):
631
+ if len(firstRec[valueIdx]) != 1:
632
+ raise Exception(
633
+ "Error writing set '"
634
+ + ecSymbol._name()
635
+ + "': Tuple representing the value needs to contain exactly one element but contains "
636
+ + str(len(firstRec[valueIdx]))
637
+ + " elements"
638
+ )
639
+ parseValues = lambda rec: rec[valueIdx][0]
640
+ else:
641
+ if len(firstRec) > valueIdx + 1:
642
+ raise Exception(
643
+ "Error writing set '"
644
+ + ecSymbol._name()
645
+ + "': Exactly one value is expected for parameter value"
646
+ )
647
+ parseValues = lambda rec: rec[valueIdx]
648
+
649
+ if len(parseKeys(firstRec)) != ecSymbol._dim:
650
+ raise Exception(
651
+ f"Error writing set '{ecSymbol._name()}': Number of keys ({len(parseKeys(firstRec))}) doesn't match the symbol dimension ({ecSymbol._dim})"
652
+ )
653
+
654
+ sawInt = False
655
+ sawString = False
656
+ for k in parseKeys(firstRec):
657
+ if isinstance(k, int):
658
+ sawInt = True
659
+ keyType = KeyType.INT
660
+ elif type(k) is str:
661
+ sawString = True
662
+ keyType = KeyType.STRING
663
+ else:
664
+ raise Exception(
665
+ "Error writing set '"
666
+ + ecSymbol._name()
667
+ + "': Expecting keys to be of type str or int"
668
+ )
669
+ if sawInt and sawString:
670
+ raise Exception(
671
+ "Error writing set '"
672
+ + ecSymbol._name()
673
+ + "': Mixed key types (int and str) are not allowed"
674
+ )
675
+ if not type(parseValues(firstRec)) is str:
676
+ raise Exception(
677
+ "Error writing set '" + ecSymbol._name() + "': Value needs to be str"
678
+ )
679
+
680
+ ecSymbol._writeSet(parseKeys, parseValues, data, keyType)
681
+
682
+ def _setPar(self, ecSymbol, data):
683
+ firstRec = next(iter(data))
684
+ keyType = KeyType.STRING
685
+
686
+ # handle scalars
687
+ if ecSymbol._dim == 0:
688
+ if len(data) != 1:
689
+ raise Exception(
690
+ "Error writing scalar '"
691
+ + ecSymbol._name()
692
+ + "': Length of data needs to be 1 but is "
693
+ + str(len(data))
694
+ )
695
+ if self._isNumeric(firstRec):
696
+ parseKeys = lambda rec: []
697
+ parseValues = lambda rec: rec
698
+ elif isinstance(firstRec, tuple):
699
+ if len(firstRec) != 1:
700
+ raise Exception(
701
+ "Error writing scalar '"
702
+ + ecSymbol._name()
703
+ + "': Length of tuple containing the scalar value needs to be 1 but is"
704
+ + str(len(firstRec))
705
+ )
706
+ if not self._isNumeric(firstRec[0]):
707
+ raise Exception(
708
+ "Error writing scalar '"
709
+ + ecSymbol._name()
710
+ + "': Element in tuple needs to be numeric"
711
+ )
712
+ parseKeys = lambda rec: []
713
+ parseValues = lambda rec: rec[0]
714
+
715
+ else:
716
+ raise Exception(
717
+ "Error writing scalar '"
718
+ + ecSymbol._name()
719
+ + "': Data format not accepted"
720
+ )
721
+
722
+ # handle parameters with dim>=1
723
+ if ecSymbol._dim > 0:
724
+ if not isinstance(firstRec, tuple):
725
+ raise Exception(
726
+ "Error writing parameter '"
727
+ + ecSymbol._name()
728
+ + "': Each record needs to be represented as a tuple"
729
+ )
730
+ if isinstance(firstRec[0], tuple):
731
+ parseKeysX = lambda rec: list(rec[0])
732
+ valueIdx = 1
733
+ else:
734
+ parseKeysX = lambda rec: list(rec[0 : ecSymbol._dim])
735
+ valueIdx = ecSymbol._dim
736
+ parseKeys = lambda rec: [ecSymbol._mapKeys(r) for r in parseKeysX(rec)]
737
+
738
+ if len(parseKeys(firstRec)) != ecSymbol._dim:
739
+ raise Exception(
740
+ f"Error writing parameter '{ecSymbol._name()}': Number of keys ({len(parseKeys(firstRec))}) doesn't match the symbol dimension ({ecSymbol._dim})"
741
+ )
742
+
743
+ if isinstance(firstRec[valueIdx], tuple):
744
+ if len(firstRec[valueIdx]) != 1:
745
+ raise Exception(
746
+ "Error writing parameter '"
747
+ + ecSymbol._name()
748
+ + "': Tuple representing the value needs to contain exactly one element but contains "
749
+ + str(len(firstRec[valueIdx]))
750
+ + " elements"
751
+ )
752
+ parseValues = lambda rec: rec[valueIdx][0]
753
+ else:
754
+ parseValues = lambda rec: rec[valueIdx]
755
+ if len(firstRec) > valueIdx + 1:
756
+ raise Exception(
757
+ "Error writing parameter '"
758
+ + ecSymbol._name()
759
+ + "': Exactly one value is expected for parameter value"
760
+ )
761
+
762
+ sawInt = False
763
+ sawString = False
764
+ for k in parseKeys(firstRec):
765
+ if isinstance(k, int):
766
+ sawInt = True
767
+ keyType = KeyType.INT
768
+ elif type(k) is str:
769
+ sawString = True
770
+ keyType = KeyType.STRING
771
+ else:
772
+ raise Exception(
773
+ "Error writing parameter '"
774
+ + ecSymbol._name()
775
+ + "': Expecting keys to be of type str or int"
776
+ )
777
+ if sawInt and sawString:
778
+ raise Exception(
779
+ "Error writing parameter '"
780
+ + ecSymbol._name()
781
+ + "': Mixed key types (int and str) are not allowed"
782
+ )
783
+ if not self._isNumeric(parseValues(firstRec)):
784
+ raise Exception(
785
+ "Error writing parameter '"
786
+ + ecSymbol._name()
787
+ + "': Value needs to be numeric"
788
+ )
789
+
790
+ ecSymbol._writeParameter(parseKeys, parseValues, data, keyType)
791
+
792
+ # TODO: combine with _setPar in order to avoid redundant code
793
+ def _setVarEqu(self, ecSymbol, data):
794
+ firstRec = next(iter(data))
795
+ keyType = KeyType.STRING
796
+
797
+ # handle scalar variables
798
+ if ecSymbol._dim == 0:
799
+ if len(data) != 1:
800
+ raise Exception(
801
+ "Error writing scalar variable/equation '"
802
+ + ecSymbol._name()
803
+ + "': Length of data needs to be 1 but is "
804
+ + str(len(data))
805
+ )
806
+ if not isinstance(firstRec, tuple):
807
+ raise Exception(
808
+ "Error writing scalar variable/equation '"
809
+ + ecSymbol._name()
810
+ + "': Record needs to be represented as tuple"
811
+ )
812
+ if not len(firstRec) == 5:
813
+ raise Exception(
814
+ "Error writing scalar variable/equation '"
815
+ + ecSymbol._name()
816
+ + "': Record needs to consist of 5 values, but found "
817
+ + str(len(firstRecord))
818
+ )
819
+ for v in firstRec:
820
+ if not self._isNumeric(v):
821
+ raise Exception(
822
+ "Error writing scalar variable/equation '"
823
+ + ecSymbol._name()
824
+ + "': Values need to be numeric"
825
+ )
826
+ parseKeys = lambda rec: []
827
+ parseValues = lambda rec: list(rec)
828
+
829
+ # handle variables with dim>=1
830
+ if ecSymbol._dim > 0:
831
+ if not isinstance(firstRec, tuple):
832
+ raise Exception(
833
+ "Error writing variable/equation '"
834
+ + ecSymbol._name()
835
+ + "': Each record needs to be represented as a tuple"
836
+ )
837
+ if isinstance(firstRec[0], tuple):
838
+ parseKeysX = lambda rec: list(rec[0])
839
+ valueIdx = 1
840
+ else:
841
+ parseKeysX = lambda rec: list(rec[0 : ecSymbol._dim])
842
+ valueIdx = ecSymbol._dim
843
+ parseKeys = lambda rec: [ecSymbol._mapKeys(r) for r in parseKeysX(rec)]
844
+
845
+ if len(parseKeys(firstRec)) != ecSymbol._dim:
846
+ raise Exception(
847
+ "Error writing variable/equation '"
848
+ + ecSymbol._name()
849
+ + "': Number of keys ("
850
+ + str(len(parseKeys(firstRec)))
851
+ + ") doesn't match the symbol dimension ("
852
+ + str(ecSymbol._dim)
853
+ )
854
+
855
+ if isinstance(firstRec[valueIdx], tuple):
856
+ if len(firstRec[valueIdx]) != 5:
857
+ raise Exception(
858
+ "Error writing variable/equation '"
859
+ + ecSymbol._name()
860
+ + "': Tuple representing the value needs to contain exactly five element but contains "
861
+ + str(len(firstRec[valueIdx]))
862
+ + " elements"
863
+ )
864
+ parseValues = lambda rec: rec[valueIdx]
865
+ else:
866
+ parseValues = lambda rec: rec[valueIdx : valueIdx + 5]
867
+ if len(firstRec) > valueIdx + 5:
868
+ raise Exception(
869
+ "Error writing variable/equation '"
870
+ + ecSymbol._name()
871
+ + "': Exactly 5 values are expected for parameter value"
872
+ )
873
+
874
+ sawInt = False
875
+ sawString = False
876
+ for k in parseKeys(firstRec):
877
+ if isinstance(k, int):
878
+ sawInt = True
879
+ keyType = KeyType.INT
880
+ elif type(k) is str:
881
+ sawString = True
882
+ keyType = KeyType.STRING
883
+ else:
884
+ raise Exception(
885
+ "Error writing variable/equation '"
886
+ + ecSymbol._name()
887
+ + "': Expecting keys to be of type str or int"
888
+ )
889
+ if sawInt and sawString:
890
+ raise Exception(
891
+ "Error writing variable/equation '"
892
+ + ecSymbol._name()
893
+ + "': Mixed key types (int and str) are not allowed"
894
+ )
895
+
896
+ for v in parseValues(firstRec):
897
+ if not self._isNumeric(v):
898
+ raise Exception(
899
+ "Error writing variable/equation '"
900
+ + ecSymbol._name()
901
+ + "': Value needs to be numeric"
902
+ )
903
+
904
+ ecSymbol._writeVarEqu(parseKeys, parseValues, data, keyType)
905
+
906
+ def _inferDimension(self, sym_type, data):
907
+ if isinstance(data, _GamsSymbol):
908
+ return data.dimension
909
+ elif len(data) == 0:
910
+ if sym_type == dt_set:
911
+ return 1
912
+ else:
913
+ return 0
914
+ else:
915
+ firstRec = next(iter(data))
916
+ if sym_type == dt_par:
917
+ if self._isNumeric(firstRec):
918
+ return 0
919
+ elif isinstance(firstRec, tuple):
920
+ if isinstance(firstRec[0], tuple):
921
+ return len(firstRec[0])
922
+ else:
923
+ return len(firstRec) - 1
924
+ else:
925
+ raise Exception("Unable to infer symbol dimension from data.")
926
+ elif sym_type == dt_set:
927
+ if isinstance(firstRec, tuple):
928
+ if isinstance(firstRec[0], tuple):
929
+ return len(firstRec[0])
930
+ elif isinstance(firstRec[-1], tuple):
931
+ return len(firstRec) - 1
932
+ else:
933
+ return len(firstRec)
934
+ else:
935
+ return 1
936
+ elif sym_type in [dt_var, dt_equ]:
937
+ if isinstance(firstRec, tuple):
938
+ if isinstance(firstRec[0], tuple):
939
+ return len(firstRec[0])
940
+ elif isinstance(firstRec[-1], tuple):
941
+ return len(firstRec) - 1
942
+ else:
943
+ return len(firstRec) - GMS_VAL_MAX
944
+ else:
945
+ raise Exception("Unable to infer symbol dimension from data.")
946
+
947
+ def set(
948
+ self,
949
+ symbolName,
950
+ data,
951
+ mergeType=MergeType.DEFAULT,
952
+ domCheck=DomainCheckType.DEFAULT,
953
+ mapKeys=lambda x: x,
954
+ dimension=None,
955
+ ):
956
+ if (
957
+ not isinstance(data, list)
958
+ and not isinstance(data, set)
959
+ and not isinstance(data, _GamsSymbol)
960
+ ):
961
+ raise Exception("Data needs to be a list or a set")
962
+ if dimension is not None and dimension not in range(GMS_MAX_INDEX_DIM + 1):
963
+ raise Exception(f"Invalid value for parameter 'dimension'. Must be an integer between 0 and 20 (inclusive) but was '{dimension}'")
964
+
965
+ try:
966
+ ecSymbol = self.get(symbolName)
967
+ except Exception:
968
+ if gmdHandleToPtr(self._gmdud) is not None:
969
+ rc = new_intp()
970
+ symPtr = gmdFindSymbolPy(self._gmdud, symbolName, rc)
971
+ if not _intValueAndFree(rc):
972
+ raise
973
+ rc, sym_type = gmdSymbolType(self._gmdud, symPtr)
974
+ self._check_for_gmd_error(rc, self._gmdud)
975
+ if dimension is None:
976
+ dimension = self._inferDimension(sym_type, data)
977
+ if dimension not in range(GMS_MAX_INDEX_DIM + 1):
978
+ raise Exception("Unable to infer symbol dimension from data.")
979
+
980
+ rc, user_info, _, _ = gmdSymbolInfo(self._gmdud, symPtr, GMD_USERINFO)
981
+ self._check_for_gmd_error(rc, self._gmdud)
982
+ rc, _, _, sym_text = gmdSymbolInfo(self._gmdud, symPtr, GMD_EXPLTEXT)
983
+ self._check_for_gmd_error(rc, self._gmdud)
984
+
985
+ rc = new_intp()
986
+ gmdAddSymbolPy(
987
+ self._gmd,
988
+ symbolName,
989
+ dimension,
990
+ sym_type,
991
+ user_info,
992
+ sym_text,
993
+ rc,
994
+ )
995
+ self._check_for_gmd_error(_intValueAndFree(rc))
996
+ ecSymbol = self.get(symbolName)
997
+
998
+ ecSymbol._mergeType = mergeType
999
+ ecSymbol._domCheck = domCheck
1000
+ ecSymbol._mapKeys = mapKeys
1001
+ if isinstance(data, _GamsSymbol):
1002
+ if (
1003
+ data._sym_ptr == ecSymbol._symPtr
1004
+ ): # same symbol, no copy of data required
1005
+ pass
1006
+ else:
1007
+ gmdCopySymbol(data._database._gmd, ecSymbol._symPtr, data._sym_ptr)
1008
+
1009
+ elif len(data) == 0:
1010
+ ecSymbol._clear()
1011
+ elif ecSymbol._type == dt_par:
1012
+ self._setPar(ecSymbol, data)
1013
+ elif ecSymbol._type in [dt_var, dt_equ]:
1014
+ self._setVarEqu(ecSymbol, data)
1015
+ elif ecSymbol._type == dt_set:
1016
+ self._setSet(ecSymbol, data)
1017
+
1018
+ self._modSymList[ecSymbol.getSymbolNumber()] = (
1019
+ merge_type_to_int(ecSymbol._mergeType),
1020
+ domcheck_type_to_int(ecSymbol._domCheck),
1021
+ )
1022
+
1023
+ def _isNumeric(self, element):
1024
+ return isinstance(element, float) or isinstance(element, int)