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.
- gams/__init__.py +27 -0
- gams/_version.py +1 -0
- gams/connect/__init__.py +28 -0
- gams/connect/agents/__init__.py +24 -0
- gams/connect/agents/_excel/__init__.py +32 -0
- gams/connect/agents/_excel/excelagent.py +312 -0
- gams/connect/agents/_excel/workbook.py +155 -0
- gams/connect/agents/_sqlconnectors/__init__.py +42 -0
- gams/connect/agents/_sqlconnectors/_accesshandler.py +211 -0
- gams/connect/agents/_sqlconnectors/_databasehandler.py +250 -0
- gams/connect/agents/_sqlconnectors/_mysqlhandler.py +168 -0
- gams/connect/agents/_sqlconnectors/_postgreshandler.py +131 -0
- gams/connect/agents/_sqlconnectors/_pyodbchandler.py +112 -0
- gams/connect/agents/_sqlconnectors/_sqlalchemyhandler.py +74 -0
- gams/connect/agents/_sqlconnectors/_sqlitehandler.py +262 -0
- gams/connect/agents/_sqlconnectors/_sqlserverhandler.py +179 -0
- gams/connect/agents/concatenate.py +440 -0
- gams/connect/agents/connectagent.py +743 -0
- gams/connect/agents/csvreader.py +675 -0
- gams/connect/agents/csvwriter.py +151 -0
- gams/connect/agents/domainwriter.py +143 -0
- gams/connect/agents/excelreader.py +756 -0
- gams/connect/agents/excelwriter.py +467 -0
- gams/connect/agents/filter.py +223 -0
- gams/connect/agents/gamsreader.py +112 -0
- gams/connect/agents/gamswriter.py +239 -0
- gams/connect/agents/gdxreader.py +109 -0
- gams/connect/agents/gdxwriter.py +146 -0
- gams/connect/agents/labelmanipulator.py +303 -0
- gams/connect/agents/projection.py +539 -0
- gams/connect/agents/pythoncode.py +71 -0
- gams/connect/agents/rawcsvreader.py +248 -0
- gams/connect/agents/rawexcelreader.py +312 -0
- gams/connect/agents/schema/CSVReader.yaml +92 -0
- gams/connect/agents/schema/CSVWriter.yaml +44 -0
- gams/connect/agents/schema/Concatenate.yaml +52 -0
- gams/connect/agents/schema/DomainWriter.yaml +25 -0
- gams/connect/agents/schema/ExcelReader.yaml +121 -0
- gams/connect/agents/schema/ExcelWriter.yaml +78 -0
- gams/connect/agents/schema/Filter.yaml +74 -0
- gams/connect/agents/schema/GAMSReader.yaml +20 -0
- gams/connect/agents/schema/GAMSWriter.yaml +47 -0
- gams/connect/agents/schema/GDXReader.yaml +23 -0
- gams/connect/agents/schema/GDXWriter.yaml +32 -0
- gams/connect/agents/schema/LabelManipulator.yaml +99 -0
- gams/connect/agents/schema/Projection.yaml +24 -0
- gams/connect/agents/schema/PythonCode.yaml +6 -0
- gams/connect/agents/schema/RawCSVReader.yaml +34 -0
- gams/connect/agents/schema/RawExcelReader.yaml +42 -0
- gams/connect/agents/schema/SQLReader.yaml +75 -0
- gams/connect/agents/schema/SQLWriter.yaml +103 -0
- gams/connect/agents/sqlreader.py +301 -0
- gams/connect/agents/sqlwriter.py +276 -0
- gams/connect/connectdatabase.py +275 -0
- gams/connect/connectvalidator.py +93 -0
- gams/connect/errors.py +34 -0
- gams/control/__init__.py +136 -0
- gams/control/database.py +2231 -0
- gams/control/execution.py +1900 -0
- gams/control/options.py +2792 -0
- gams/control/workspace.py +1198 -0
- gams/core/__init__.py +24 -0
- gams/core/cfg/__init__.py +26 -0
- gams/core/cfg/_cfgmcc.cp312-win_amd64.pyd +0 -0
- gams/core/cfg/cfgmcc.py +519 -0
- gams/core/dct/__init__.py +26 -0
- gams/core/dct/_dctmcc.cp312-win_amd64.pyd +0 -0
- gams/core/dct/dctmcc.py +574 -0
- gams/core/embedded/__init__.py +26 -0
- gams/core/embedded/gamsemb.py +1024 -0
- gams/core/emp/__init__.py +24 -0
- gams/core/emp/emplexer.py +89 -0
- gams/core/emp/empyacc.py +281 -0
- gams/core/gdx/__init__.py +26 -0
- gams/core/gdx/_gdxcc.cp312-win_amd64.pyd +0 -0
- gams/core/gdx/gdxcc.py +866 -0
- gams/core/gev/__init__.py +26 -0
- gams/core/gev/_gevmcc.cp312-win_amd64.pyd +0 -0
- gams/core/gev/gevmcc.py +855 -0
- gams/core/gmd/__init__.py +26 -0
- gams/core/gmd/_gmdcc.cp312-win_amd64.pyd +0 -0
- gams/core/gmd/gmdcc.py +917 -0
- gams/core/gmo/__init__.py +26 -0
- gams/core/gmo/_gmomcc.cp312-win_amd64.pyd +0 -0
- gams/core/gmo/gmomcc.py +2046 -0
- gams/core/idx/__init__.py +26 -0
- gams/core/idx/_idxcc.cp312-win_amd64.pyd +0 -0
- gams/core/idx/idxcc.py +510 -0
- gams/core/numpy/__init__.py +29 -0
- gams/core/numpy/_gams2numpy.cp312-win_amd64.pyd +0 -0
- gams/core/numpy/gams2numpy.py +1048 -0
- gams/core/opt/__init__.py +26 -0
- gams/core/opt/_optcc.cp312-win_amd64.pyd +0 -0
- gams/core/opt/optcc.py +840 -0
- gams/engine/__init__.py +204 -0
- gams/engine/api/__init__.py +13 -0
- gams/engine/api/auth_api.py +7653 -0
- gams/engine/api/cleanup_api.py +751 -0
- gams/engine/api/default_api.py +887 -0
- gams/engine/api/hypercube_api.py +2629 -0
- gams/engine/api/jobs_api.py +5229 -0
- gams/engine/api/licenses_api.py +2220 -0
- gams/engine/api/namespaces_api.py +7783 -0
- gams/engine/api/usage_api.py +5627 -0
- gams/engine/api/users_api.py +5931 -0
- gams/engine/api_client.py +804 -0
- gams/engine/api_response.py +21 -0
- gams/engine/configuration.py +601 -0
- gams/engine/exceptions.py +216 -0
- gams/engine/models/__init__.py +86 -0
- gams/engine/models/bad_input.py +89 -0
- gams/engine/models/cleanable_job_result.py +104 -0
- gams/engine/models/cleanable_job_result_page.py +113 -0
- gams/engine/models/engine_license.py +107 -0
- gams/engine/models/files_not_found.py +93 -0
- gams/engine/models/forwarded_token_response.py +112 -0
- gams/engine/models/generic_key_value_pair.py +89 -0
- gams/engine/models/hypercube.py +160 -0
- gams/engine/models/hypercube_page.py +111 -0
- gams/engine/models/hypercube_summary.py +91 -0
- gams/engine/models/hypercube_token.py +97 -0
- gams/engine/models/identity_provider.py +107 -0
- gams/engine/models/identity_provider_ldap.py +121 -0
- gams/engine/models/identity_provider_oauth2.py +146 -0
- gams/engine/models/identity_provider_oauth2_scope.py +89 -0
- gams/engine/models/identity_provider_oauth2_with_secret.py +152 -0
- gams/engine/models/identity_provider_oidc.py +133 -0
- gams/engine/models/identity_provider_oidc_with_secret.py +143 -0
- gams/engine/models/inex.py +91 -0
- gams/engine/models/invitation.py +136 -0
- gams/engine/models/invitation_quota.py +106 -0
- gams/engine/models/invitation_token.py +87 -0
- gams/engine/models/job.py +165 -0
- gams/engine/models/job_no_text_entry.py +138 -0
- gams/engine/models/job_no_text_entry_page.py +111 -0
- gams/engine/models/license.py +91 -0
- gams/engine/models/log_piece.py +96 -0
- gams/engine/models/message.py +87 -0
- gams/engine/models/message_and_token.py +99 -0
- gams/engine/models/message_with_webhook_id.py +89 -0
- gams/engine/models/model_auth_token.py +87 -0
- gams/engine/models/model_configuration.py +125 -0
- gams/engine/models/model_default_instance.py +99 -0
- gams/engine/models/model_default_user_instance.py +98 -0
- gams/engine/models/model_hypercube_job.py +106 -0
- gams/engine/models/model_hypercube_usage.py +130 -0
- gams/engine/models/model_instance_info.py +116 -0
- gams/engine/models/model_instance_info_full.py +123 -0
- gams/engine/models/model_instance_pool_info.py +112 -0
- gams/engine/models/model_job_labels.py +179 -0
- gams/engine/models/model_job_usage.py +133 -0
- gams/engine/models/model_pool_usage.py +124 -0
- gams/engine/models/model_usage.py +115 -0
- gams/engine/models/model_user.py +96 -0
- gams/engine/models/model_userinstance_info.py +119 -0
- gams/engine/models/model_userinstancepool_info.py +95 -0
- gams/engine/models/model_version.py +91 -0
- gams/engine/models/models.py +120 -0
- gams/engine/models/namespace.py +104 -0
- gams/engine/models/namespace_quota.py +96 -0
- gams/engine/models/namespace_with_permission.py +96 -0
- gams/engine/models/not_found.py +91 -0
- gams/engine/models/password_policy.py +97 -0
- gams/engine/models/perm_and_username.py +89 -0
- gams/engine/models/quota.py +117 -0
- gams/engine/models/quota_exceeded.py +97 -0
- gams/engine/models/status_code_meaning.py +89 -0
- gams/engine/models/stream_entry.py +89 -0
- gams/engine/models/system_wide_license.py +92 -0
- gams/engine/models/text_entries.py +87 -0
- gams/engine/models/text_entry.py +101 -0
- gams/engine/models/time_span.py +95 -0
- gams/engine/models/time_span_pool_worker.py +99 -0
- gams/engine/models/token_forward_error.py +87 -0
- gams/engine/models/user.py +127 -0
- gams/engine/models/user_group_member.py +96 -0
- gams/engine/models/user_groups.py +108 -0
- gams/engine/models/vapid_info.py +87 -0
- gams/engine/models/webhook.py +138 -0
- gams/engine/models/webhook_parameterized_event.py +99 -0
- gams/engine/py.typed +0 -0
- gams/engine/rest.py +258 -0
- gams/magic/__init__.py +32 -0
- gams/magic/gams_magic.py +142 -0
- gams/magic/interactive.py +402 -0
- gams/tools/__init__.py +30 -0
- gams/tools/errors.py +34 -0
- gams/tools/toolcollection/__init__.py +24 -0
- gams/tools/toolcollection/alg/__init__.py +24 -0
- gams/tools/toolcollection/alg/rank.py +51 -0
- gams/tools/toolcollection/data/__init__.py +24 -0
- gams/tools/toolcollection/data/csvread.py +444 -0
- gams/tools/toolcollection/data/csvwrite.py +311 -0
- gams/tools/toolcollection/data/exceldump.py +47 -0
- gams/tools/toolcollection/data/sqlitewrite.py +276 -0
- gams/tools/toolcollection/gdxservice/__init__.py +24 -0
- gams/tools/toolcollection/gdxservice/gdxencoding.py +104 -0
- gams/tools/toolcollection/gdxservice/gdxrename.py +94 -0
- gams/tools/toolcollection/linalg/__init__.py +24 -0
- gams/tools/toolcollection/linalg/cholesky.py +57 -0
- gams/tools/toolcollection/linalg/eigenvalue.py +56 -0
- gams/tools/toolcollection/linalg/eigenvector.py +58 -0
- gams/tools/toolcollection/linalg/invert.py +55 -0
- gams/tools/toolcollection/linalg/ols.py +138 -0
- gams/tools/toolcollection/tooltemplate.py +321 -0
- gams/tools/toolcollection/win32/__init__.py +24 -0
- gams/tools/toolcollection/win32/excelmerge.py +93 -0
- gams/tools/toolcollection/win32/exceltalk.py +76 -0
- gams/tools/toolcollection/win32/msappavail.py +49 -0
- gams/tools/toolcollection/win32/shellexecute.py +54 -0
- gams/tools/tools.py +116 -0
- gams/transfer/__init__.py +35 -0
- gams/transfer/_abcs/__init__.py +37 -0
- gams/transfer/_abcs/container_abcs.py +433 -0
- gams/transfer/_internals/__init__.py +63 -0
- gams/transfer/_internals/algorithms.py +436 -0
- gams/transfer/_internals/casepreservingdict.py +124 -0
- gams/transfer/_internals/constants.py +270 -0
- gams/transfer/_internals/domainviolation.py +103 -0
- gams/transfer/_internals/specialvalues.py +172 -0
- gams/transfer/containers/__init__.py +26 -0
- gams/transfer/containers/_container.py +1794 -0
- gams/transfer/containers/_io/__init__.py +28 -0
- gams/transfer/containers/_io/containers.py +164 -0
- gams/transfer/containers/_io/gdx.py +1029 -0
- gams/transfer/containers/_io/gmd.py +872 -0
- gams/transfer/containers/_mixins/__init__.py +26 -0
- gams/transfer/containers/_mixins/ccc.py +1274 -0
- gams/transfer/syms/__init__.py +33 -0
- gams/transfer/syms/_methods/__init__.py +24 -0
- gams/transfer/syms/_methods/tables.py +120 -0
- gams/transfer/syms/_methods/toDict.py +115 -0
- gams/transfer/syms/_methods/toList.py +83 -0
- gams/transfer/syms/_methods/toValue.py +60 -0
- gams/transfer/syms/_mixins/__init__.py +32 -0
- gams/transfer/syms/_mixins/equals.py +626 -0
- gams/transfer/syms/_mixins/generateRecords.py +499 -0
- gams/transfer/syms/_mixins/pivot.py +313 -0
- gams/transfer/syms/_mixins/pve.py +627 -0
- gams/transfer/syms/_mixins/sa.py +27 -0
- gams/transfer/syms/_mixins/sapve.py +27 -0
- gams/transfer/syms/_mixins/saua.py +27 -0
- gams/transfer/syms/_mixins/sauapve.py +199 -0
- gams/transfer/syms/_mixins/spve.py +1528 -0
- gams/transfer/syms/_mixins/ve.py +936 -0
- gams/transfer/syms/container_syms/__init__.py +31 -0
- gams/transfer/syms/container_syms/_alias.py +984 -0
- gams/transfer/syms/container_syms/_equation.py +333 -0
- gams/transfer/syms/container_syms/_parameter.py +973 -0
- gams/transfer/syms/container_syms/_set.py +604 -0
- gams/transfer/syms/container_syms/_universe_alias.py +461 -0
- gams/transfer/syms/container_syms/_variable.py +321 -0
- gamsapi-52.5.0.dist-info/METADATA +150 -0
- gamsapi-52.5.0.dist-info/RECORD +257 -0
- gamsapi-52.5.0.dist-info/WHEEL +5 -0
- gamsapi-52.5.0.dist-info/licenses/LICENSE +22 -0
- 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)
|