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,973 @@
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
+ import copy
27
+ import itertools
28
+ import weakref
29
+ import pandas as pd
30
+ from pandas.api.types import (
31
+ CategoricalDtype,
32
+ infer_dtype,
33
+ )
34
+ import numpy as np
35
+ from gams.core import gdx
36
+ from gams.transfer._abcs import ABCParameter, ABCSet, ABCContainer
37
+ from gams.transfer.syms._mixins import PVEMixin, SAPVEMixin, SAUAPVEMixin, SPVEMixin
38
+ from gams.transfer._internals import (
39
+ generate_unique_labels,
40
+ cartesian_product,
41
+ EPS,
42
+ SpecialValues,
43
+ UNDEF,
44
+ NA,
45
+ )
46
+
47
+ from gams.transfer.syms._mixins.pivot import PivotParameterMixin
48
+ from gams.transfer.syms._mixins.generateRecords import GenerateRecordsParameterMixin
49
+ from gams.transfer.syms._mixins.equals import EqualsParameterMixin
50
+ from typing import Any, Optional, Union, TYPE_CHECKING
51
+
52
+ if TYPE_CHECKING:
53
+ from gams.transfer import Container
54
+
55
+ class Parameter(
56
+ PVEMixin,
57
+ SAPVEMixin,
58
+ SAUAPVEMixin,
59
+ SPVEMixin,
60
+ PivotParameterMixin,
61
+ GenerateRecordsParameterMixin,
62
+ EqualsParameterMixin,
63
+ ABCParameter,
64
+ ):
65
+ """
66
+ Represents a parameter symbol in GAMS. https://www.gams.com/latest/docs/UG_DataEntry.html#UG_DataEntry_Parameters
67
+
68
+ Parameters
69
+ ----------
70
+ container : Container
71
+ name : str
72
+ domain : list, optional
73
+ records : Any, optional
74
+ domain_forwarding : bool, optional
75
+ description : str, optional
76
+
77
+ Examples
78
+ --------
79
+ >>> import gams.transfer as gt
80
+ >>> m = gt.Container()
81
+ >>> i = gt.Set(m, "i", records=['i1','i2'])
82
+ >>> a = gt.Parameter(m, "a", [i], records=[['i1',1],['i2',2]])
83
+
84
+ Attributes
85
+ ----------
86
+ container : Container object
87
+ Container where the symbol exists
88
+ description : str
89
+ description of symbol
90
+ dimension : int
91
+ The dimension of symbol
92
+ domain : List[Set | Alias | str]
93
+ List of domains given either as string (* for universe set) or as reference to the Set/Alias object
94
+ domain_forwarding : bool
95
+ Flag that identifies if domain forwarding is enabled for the symbol
96
+ domain_labels : List[str]
97
+ The column headings for the records DataFrame
98
+ domain_names : List[str]
99
+ String version of domain names
100
+ domain_type : str
101
+ The state of domain links
102
+ is_scalar : bool
103
+ Flag that identifies if the Parameter is scalar
104
+ modified: bool
105
+ Flag that identifies if the symbol has been modified
106
+ name : str
107
+ Name of the symbol
108
+ number_records : int
109
+ The number of symbol records
110
+ records : DataFrame
111
+ The main symbol records
112
+ shape : tuple
113
+ Shape of symbol records
114
+ summary : dict
115
+ A dict of only the metadata
116
+ """
117
+
118
+ @classmethod
119
+ def _from_gams(cls, container, name, domain, records=None, description=""):
120
+ # create new symbol object
121
+ obj = Parameter.__new__(cls)
122
+
123
+ # set private properties directly
124
+ obj._requires_state_check = False
125
+ obj._container = weakref.proxy(container)
126
+ obj._name = name
127
+ obj._domain = domain
128
+ obj._domain_forwarding = False
129
+ obj._description = description
130
+ obj._records = records
131
+ obj._modified = True
132
+
133
+ # typing
134
+ obj._gams_type = gdx.GMS_DT_PAR
135
+ obj._gams_subtype = 0
136
+
137
+ # add to container
138
+ obj._container.data.update({name: obj})
139
+ obj._container._requires_state_check = True
140
+
141
+ return obj
142
+
143
+ def __new__(cls, *args, **kwargs):
144
+ # fastpath
145
+ if len(args) == len(kwargs) == 0:
146
+ return object.__new__(cls)
147
+
148
+ try:
149
+ container = args[0]
150
+ except IndexError:
151
+ container = kwargs.get("container", None)
152
+
153
+ try:
154
+ name = args[1]
155
+ except IndexError:
156
+ name = kwargs.get("name", None)
157
+
158
+ try:
159
+ symobj = container[name]
160
+ except (KeyError, IndexError, TypeError):
161
+ symobj = None
162
+
163
+ if symobj is None:
164
+ return object.__new__(cls)
165
+ else:
166
+ if isinstance(symobj, cls):
167
+ return symobj
168
+ else:
169
+ raise TypeError(
170
+ f"Cannot overwrite symbol '{symobj.name}' in container because it is not a {cls.__name__} object"
171
+ )
172
+
173
+ def __init__(
174
+ self,
175
+ container: "Container",
176
+ name: str,
177
+ domain: Optional[list] = None,
178
+ records: Optional[Any] = None,
179
+ domain_forwarding: bool = False,
180
+ description: str = "",
181
+ uels_on_axes: bool = False,
182
+ ):
183
+ # domain handling
184
+ if domain is None:
185
+ domain = []
186
+
187
+ if isinstance(domain, (ABCSet, str)):
188
+ domain = [domain]
189
+
190
+ # does symbol exist
191
+ has_symbol = False
192
+ if isinstance(getattr(self, "container", None), ABCContainer):
193
+ has_symbol = True
194
+
195
+ if has_symbol:
196
+ try:
197
+ if any(
198
+ d1 != d2 for d1, d2 in itertools.zip_longest(self.domain, domain)
199
+ ):
200
+ raise ValueError(
201
+ "Cannot overwrite symbol in container unless symbol domains are equal"
202
+ )
203
+
204
+ if self.domain_forwarding != domain_forwarding:
205
+ raise ValueError(
206
+ "Cannot overwrite symbol in container unless 'domain_forwarding' is left unchanged"
207
+ )
208
+
209
+ except ValueError as err:
210
+ raise ValueError(err)
211
+
212
+ except TypeError as err:
213
+ raise TypeError(err)
214
+
215
+ # reset some properties
216
+ self._requires_state_check = True
217
+ self.container._requires_state_check = True
218
+ if description != "":
219
+ self.description = description
220
+ self.records = None
221
+ self.modified = True
222
+
223
+ # only set records if records are provided
224
+ if records is not None:
225
+ self.setRecords(records, uels_on_axes=uels_on_axes)
226
+
227
+ else:
228
+ # populate new symbol properties
229
+ self._requires_state_check = True
230
+ self.container = container
231
+ self.container._requires_state_check = True
232
+ self.name = name
233
+ self.domain = domain
234
+ self.domain_forwarding = domain_forwarding
235
+ self.description = description
236
+ self.records = None
237
+ self.modified = True
238
+
239
+ # typing
240
+ self._gams_type = gdx.GMS_DT_PAR
241
+ self._gams_subtype = 0
242
+
243
+ # only set records if records are provided
244
+ if records is not None:
245
+ self.setRecords(records, uels_on_axes=uels_on_axes)
246
+
247
+ # add to container
248
+ container.data.update({name: self})
249
+
250
+ def __repr__(self):
251
+ return f"<Parameter `{self.name}` ({hex(id(self))})>"
252
+
253
+ def __delitem__(self):
254
+ del self.container.data[self.name]
255
+
256
+ @property
257
+ def _attributes(self):
258
+ return ["value"]
259
+
260
+ @property
261
+ def summary(self) -> dict:
262
+ """
263
+ Returns a dict of only the metadata
264
+
265
+ Returns
266
+ -------
267
+ dict
268
+ Outputs a dict of only the metadata
269
+ """
270
+ return {
271
+ "name": self.name,
272
+ "description": self.description,
273
+ "domain": self.domain_names,
274
+ "domain_type": self.domain_type,
275
+ "dimension": self.dimension,
276
+ "number_records": self.number_records,
277
+ }
278
+
279
+ def toValue(self) -> Union[float, None]:
280
+ """
281
+ Convenience method to return symbol records as a python float. Only possible with scalar symbols.
282
+
283
+ Returns
284
+ -------
285
+ float | None
286
+ Scalar's record, None if no record was assigned
287
+ """
288
+ from gams.transfer.syms._methods.toValue import toValueParameter
289
+
290
+ if not self.isValid():
291
+ raise Exception(
292
+ f"Cannot extract value because `{self.name}` is not a valid symbol object. "
293
+ f"Use `{self.name}.isValid(verbose=True)` to debug."
294
+ )
295
+
296
+ return toValueParameter(self)
297
+
298
+ def toList(self) -> Union[list, None]:
299
+ """
300
+ Convenience method to return symbol records as a python list
301
+
302
+ Returns
303
+ -------
304
+ list | None
305
+ A list of symbol records, None if no records were assigned
306
+ """
307
+ from gams.transfer.syms._methods.toList import toListParameter
308
+
309
+ if not self.isValid():
310
+ raise Exception(
311
+ f"Cannot extract list because `{self.name}` is not a valid symbol object. "
312
+ f"Use `{self.name}.isValid(verbose=True)` to debug."
313
+ )
314
+ return toListParameter(self)
315
+
316
+ def toDict(self, orient: Optional[str] = None) -> Union[dict, None]:
317
+ """
318
+ convenience method to return symbol records as a python dictionary, orient can take values natural or columns and will control the shape of the dict.
319
+ Must use orient="columns" if attempting to set symbol records with setRecords
320
+
321
+ Parameters
322
+ ----------
323
+ orient : str, optional
324
+ Takes 'natural' or 'columns', by default None which sets it to 'natural'.
325
+
326
+ Returns
327
+ -------
328
+ dict | None
329
+ A dictionary with symbol records, None if no records were assigned
330
+
331
+ Examples
332
+ --------
333
+ >>> m = gt.Container()
334
+ >>> j = gt.Set(m, "j", records=["new-york", "chicago", "topeka"])
335
+ >>> s = gt.Parameter(m, "s", [j], records=np.array([3,4,5]))
336
+ >>> print(s.toDict(orient="natural"))
337
+ {'new-york': 3.0, 'chicago': 4.0, 'topeka': 5.0}
338
+ >>> print(s.toDict(orient="columns"))
339
+ {'j': {0: 'new-york', 1: 'chicago', 2: 'topeka'}, 'value': {0: 3.0, 1: 4.0, 2: 5.0}}
340
+ """
341
+ from gams.transfer.syms._methods.toDict import toDictParameter
342
+
343
+ if not self.isValid():
344
+ raise Exception(
345
+ f"Cannot extract dict because `{self.name}` is not a valid symbol object. "
346
+ f"Use `{self.name}.isValid(verbose=True)` to debug."
347
+ )
348
+
349
+ return toDictParameter(self, orient=orient)
350
+
351
+ def dropZeros(self) -> None:
352
+ """
353
+ Main convenience method to remove zero values from the symbol's records.
354
+ """
355
+ mask = (self.records.iloc[:, -1] == 0.0) & (
356
+ ~SpecialValues.isEps(self.records.iloc[:, -1])
357
+ )
358
+ self.records = self.records.loc[~mask, :].reset_index(drop=True)
359
+
360
+ def dropDefaults(self) -> None:
361
+ """
362
+ Main convenience method to remove zero values from the symbol's records.
363
+ """
364
+ self.dropZeros()
365
+
366
+ def dropEps(self) -> None:
367
+ """
368
+ Main convenience method to remove epsilon values from the symbol's records.
369
+ """
370
+ mask = pd.Series(SpecialValues.isEps(self.records.iloc[:, -1]), dtype=bool)
371
+ self.records = self.records[~mask].reset_index(drop=True)
372
+
373
+ def dropNA(self) -> None:
374
+ """
375
+ Main convenience method to remove NA (Not Available) values from the symbol's records.
376
+ """
377
+ mask = pd.Series(SpecialValues.isNA(self.records.iloc[:, -1]), dtype=bool)
378
+ self.records = self.records[~mask].reset_index(drop=True)
379
+
380
+ def dropUndef(self) -> None:
381
+ """
382
+ Main convenience method to remove undefined values from the symbol's records.
383
+ """
384
+ mask = pd.Series(SpecialValues.isUndef(self.records.iloc[:, -1]), dtype=bool)
385
+ self.records = self.records[~mask].reset_index(drop=True)
386
+
387
+ def dropMissing(self) -> None:
388
+ """
389
+ Main convenience method to remove missing values from the symbol's records.
390
+ """
391
+ mask = pd.Series(pd.isna(self.records.iloc[:, -1]), dtype=bool)
392
+ self.records = self.records[~mask].reset_index(drop=True)
393
+
394
+ def setRecords(self, records: Any, uels_on_axes: bool = False) -> None:
395
+ """
396
+ main convenience method to set standard pandas.DataFrame formatted records.
397
+ If uels_on_axes=True setRecords will assume that all domain information is contained in the axes of the pandas object – data will be flattened (if necessary).
398
+
399
+ Parameters
400
+ ----------
401
+ records : Any
402
+ uels_on_axes : bool, optional
403
+ """
404
+ if not isinstance(uels_on_axes, bool):
405
+ raise TypeError("Argument 'uels_on_axes' must be type bool.")
406
+
407
+ if isinstance(records, (int, float)):
408
+ self._from_int_float(records)
409
+
410
+ elif isinstance(records, np.ndarray):
411
+ self._from_ndarray(records)
412
+
413
+ elif isinstance(records, pd.DataFrame):
414
+ self._from_dataframe(records, uels_on_axes=uels_on_axes)
415
+
416
+ elif isinstance(records, pd.Series):
417
+ self._from_series(records)
418
+
419
+ else:
420
+ self._from_else(records)
421
+
422
+ def _from_series(self, records: pd.Series):
423
+ from gams.transfer.syms._methods.tables import (
424
+ _assert_axes_no_nans,
425
+ _get_implied_dimension_from_axes,
426
+ _flatten_and_convert,
427
+ )
428
+
429
+ records = copy.deepcopy(records)
430
+
431
+ # check if index has NaNs
432
+ try:
433
+ _assert_axes_no_nans(records)
434
+ except Exception as err:
435
+ raise err
436
+
437
+ # check dimensionality of data
438
+ if self.is_scalar:
439
+ if records.size == 1:
440
+ records = pd.DataFrame(records)
441
+ self._from_dataframe(records)
442
+
443
+ else:
444
+ raise Exception(
445
+ f"Attempting to set records for a scalar symbol but records.size > 1. "
446
+ "pandas.Series must have size exactly = 1 before setting records. "
447
+ "(Note: pandas.Series.index is ignored for scalar symbols)"
448
+ )
449
+
450
+ else:
451
+ dim = _get_implied_dimension_from_axes(records)
452
+ if dim != self.dimension:
453
+ raise Exception(
454
+ f"Dimensionality of data ({dim}) is inconsistent "
455
+ f"with domain specification ({self.dimension})"
456
+ )
457
+
458
+ # flatten and convert to categorical
459
+ records = _flatten_and_convert(records)
460
+
461
+ # remap special values (str -> float)
462
+ records = self._remap_str_special_values(records)
463
+
464
+ # convert data column to type float
465
+ if not isinstance(records.iloc[:, -1].dtype, float):
466
+ records.isetitem(-1, records.iloc[:, -1].astype(float))
467
+
468
+ # reset columns
469
+ records.columns = (
470
+ generate_unique_labels(self.domain_names) + self._attributes
471
+ )
472
+
473
+ # set records
474
+ self.records = records
475
+
476
+ def _from_dataframe(self, records: pd.DataFrame, uels_on_axes: bool = False):
477
+ if self.is_scalar:
478
+ self._from_flat_dataframe(records)
479
+
480
+ else:
481
+ if uels_on_axes:
482
+ self._from_table_dataframe(records)
483
+ else:
484
+ self._from_flat_dataframe(records)
485
+
486
+ def _from_flat_dataframe(self, records: pd.DataFrame):
487
+ records = pd.DataFrame(copy.deepcopy(records))
488
+
489
+ # check dimensionality of data
490
+ r, c = records.shape
491
+ if c - 1 != self.dimension:
492
+ raise Exception(
493
+ f"Dimensionality of records ({c - 1}) is inconsistent "
494
+ f"with parameter domain specification ({self.dimension})"
495
+ )
496
+
497
+ if self.is_scalar and r > 1:
498
+ raise Exception(
499
+ f"Attempting to set {r} records for a scalar symbol. "
500
+ f"Must define a domain for symbol `{self.name}` in order to set multiple records."
501
+ )
502
+
503
+ # keep user defined categories if provided
504
+ for i in range(self.dimension):
505
+ # create categorical
506
+ if not isinstance(records.iloc[:, i].dtype, CategoricalDtype):
507
+ records.isetitem(
508
+ i,
509
+ records.iloc[:, i].astype(
510
+ CategoricalDtype(
511
+ categories=records.iloc[:, i].unique(),
512
+ ordered=True,
513
+ )
514
+ ),
515
+ )
516
+
517
+ # capture user categories
518
+ old_cats = records.iloc[:, i].cat.categories.tolist()
519
+ is_ordered = records.iloc[:, i].cat.ordered
520
+
521
+ # convert any non-str categories to str, strip trailing white-space and de-dup
522
+ new_cats = list(dict.fromkeys(list(map(str.rstrip, map(str, old_cats)))))
523
+
524
+ # if categories are not unique after strip then need to remake the categorical
525
+ if len(old_cats) != len(new_cats):
526
+ # convert data to str, strip white-space and make categorical
527
+ records.isetitem(
528
+ i,
529
+ records.iloc[:, i]
530
+ .astype(str)
531
+ .map(str.rstrip)
532
+ .astype(CategoricalDtype(categories=new_cats, ordered=is_ordered)),
533
+ )
534
+
535
+ else:
536
+ # only need to rename the categories
537
+ records.isetitem(i, records.iloc[:, i].cat.rename_categories(new_cats))
538
+
539
+ # remap special values (str -> float)
540
+ records = self._remap_str_special_values(records)
541
+
542
+ # convert data column to type float if needed
543
+ if not isinstance(records.iloc[:, -1].dtype, float):
544
+ records.isetitem(-1, records.iloc[:, -1].astype(float))
545
+
546
+ # reset columns
547
+ records.columns = (
548
+ generate_unique_labels(records.columns[: self.dimension].tolist())
549
+ + self._attributes
550
+ )
551
+
552
+ # set records
553
+ self.records = records
554
+
555
+ def _from_table_dataframe(self, records: pd.DataFrame):
556
+ from gams.transfer.syms._methods.tables import (
557
+ _assert_axes_no_nans,
558
+ _get_implied_dimension_from_axes,
559
+ _flatten_and_convert,
560
+ )
561
+
562
+ records = pd.DataFrame(copy.deepcopy(records))
563
+
564
+ # check if index has NaNs
565
+ try:
566
+ _assert_axes_no_nans(records)
567
+ except Exception as err:
568
+ raise err
569
+
570
+ # check dimensionality of data
571
+ dim = _get_implied_dimension_from_axes(records)
572
+ if dim != self.dimension:
573
+ raise Exception(
574
+ f"Dimensionality of table ({dim}) is inconsistent "
575
+ f"with parameter domain specification ({self.dimension})"
576
+ )
577
+
578
+ # flatten and convert to categorical
579
+ records = _flatten_and_convert(records)
580
+
581
+ # remap special values (str -> float)
582
+ records = self._remap_str_special_values(records)
583
+
584
+ # convert data column to type float
585
+ if not isinstance(records.iloc[:, -1].dtype, float):
586
+ records.isetitem(-1, records.iloc[:, -1].astype(float))
587
+
588
+ # reset column names
589
+ records.columns = generate_unique_labels(self.domain_names) + self._attributes
590
+
591
+ # set records
592
+ self.records = records
593
+
594
+ def _from_int_float(self, records: Union[int, float]):
595
+ if not self.is_scalar:
596
+ raise Exception(
597
+ "Attempting to set a record with a scalar value, however the "
598
+ "symbol is not currently defined as a scalar (i.e., <symbol>.is_scalar == False)"
599
+ )
600
+
601
+ # note we do not drop zeros when setting
602
+ records = pd.DataFrame([records], dtype=float, columns=self._attributes)
603
+
604
+ # set records
605
+ self.records = records
606
+
607
+ def _from_ndarray(self, records: np.ndarray):
608
+ try:
609
+ records = np.array(records, dtype=float)
610
+ except Exception as err:
611
+ raise Exception(
612
+ f"Attempted conversion to numpy array (dtype=float) failed. Reason {err}"
613
+ )
614
+
615
+ # user flexibility for (n,1) and (1,n) arrays (auto reshape)
616
+ if self.dimension == 1 and (
617
+ records.shape == (1, records.size) or records.shape == (records.size, 1)
618
+ ):
619
+ records = records.reshape((records.size,))
620
+
621
+ # check dimension of array and symbol
622
+ if records.ndim != self.dimension:
623
+ raise Exception(
624
+ f"Attempting to set records for a {self.dimension}-dimensional "
625
+ f"symbol with a numpy array that is {records.ndim}-dimensional "
626
+ "-- array reshape necessary. (Note: gams.transfer will auto "
627
+ "reshape array if symbol is 1D and array is either (1,n) or (n,1))"
628
+ )
629
+
630
+ # records must have regular domain_type if not a scalar
631
+ if records.ndim > 0 and self.domain_type != "regular":
632
+ raise Exception(
633
+ "Data conversion for non-scalar array (i.e., matrix) format into "
634
+ "records is only possible for symbols where "
635
+ "self.domain_type = `regular`. "
636
+ "Must define symbol with specific domain set objects, "
637
+ f"symbol domain_type is currently `{self.domain_type}`."
638
+ )
639
+
640
+ # make sure array has the proper shape
641
+ if records.shape != self.shape:
642
+ raise Exception(
643
+ f"User passed array with shape `{records.shape}` but anticipated "
644
+ f"shape was `{self.shape}` based "
645
+ "on domain set information -- "
646
+ "must reconcile before array-to-records conversion is possible."
647
+ )
648
+
649
+ # check that all domains are valid
650
+ for i in self.domain:
651
+ if not i.isValid():
652
+ raise Exception(
653
+ f"Domain set `{i.name}` is invalid and cannot "
654
+ "be used to convert array-to-records. "
655
+ "Use `<symbol>.isValid(verbose=True)` to debug "
656
+ "this domain set symbol before proceeding."
657
+ )
658
+
659
+ # create array of codes
660
+ codes = [np.arange(len(d.getUELs(ignore_unused=True))) for d in self.domain]
661
+
662
+ # create dataframe
663
+ if self.is_scalar:
664
+ df = pd.DataFrame(index=[0])
665
+ else:
666
+ df = pd.DataFrame(cartesian_product(*tuple(codes)))
667
+
668
+ # create categoricals
669
+ for n, d in enumerate(self.domain):
670
+ dtype = CategoricalDtype(
671
+ categories=d.records.iloc[:, 0].cat.categories,
672
+ ordered=d.records.iloc[:, 0].cat.ordered,
673
+ )
674
+
675
+ df.isetitem(
676
+ n, pd.Categorical.from_codes(codes=df.iloc[:, n], dtype=dtype)
677
+ )
678
+
679
+ # insert matrix elements
680
+ df["value"] = records.reshape(-1, 1)
681
+
682
+ # drop zeros and reset index
683
+ df = self._filter_zero_records(df)
684
+
685
+ # reset column names
686
+ df.columns = generate_unique_labels(self.domain_names) + self._attributes
687
+
688
+ # set records
689
+ self.records = df
690
+
691
+ def _from_else(self, records: Any):
692
+ try:
693
+ records = pd.DataFrame(records)
694
+ except Exception as err:
695
+ raise Exception(
696
+ "Data structure passed as argument 'records' could not be "
697
+ f"successfully converted into a pandas DataFrame (reason: {err})."
698
+ )
699
+
700
+ # check dimensionality of data
701
+ r, c = records.shape
702
+ if c - 1 != self.dimension:
703
+ raise Exception(
704
+ f"Dimensionality of records ({c - 1}) is inconsistent "
705
+ f"with parameter domain specification ({self.dimension})"
706
+ )
707
+
708
+ if self.is_scalar and r > 1:
709
+ raise Exception(
710
+ f"Attempting to set {r} values for a scalar symbol. "
711
+ f"Must define a domain for symbol `{self.name}` in order to set multiple records."
712
+ )
713
+
714
+ # keep user defined categories if provided
715
+ for i in range(self.dimension):
716
+ # create categorical
717
+ if not isinstance(records.iloc[:, i].dtype, CategoricalDtype):
718
+ records.isetitem(
719
+ i,
720
+ records.iloc[:, i].astype(
721
+ CategoricalDtype(
722
+ categories=records.iloc[:, i].unique(),
723
+ ordered=True,
724
+ )
725
+ ),
726
+ )
727
+
728
+ # capture user categories
729
+ old_cats = records.iloc[:, i].cat.categories.tolist()
730
+ is_ordered = records.iloc[:, i].cat.ordered
731
+
732
+ # convert any non-str categories to str, strip trailing white-space and de-dup
733
+ new_cats = list(dict.fromkeys(list(map(str.rstrip, map(str, old_cats)))))
734
+
735
+ # if categories are not unique after strip then need to remake the categorical
736
+ if len(old_cats) != len(new_cats):
737
+ # convert data to str, strip white-space and make categorical
738
+ records.isetitem(
739
+ i,
740
+ records.iloc[:, i]
741
+ .astype(str)
742
+ .map(str.rstrip)
743
+ .astype(CategoricalDtype(categories=new_cats, ordered=is_ordered)),
744
+ )
745
+
746
+ else:
747
+ # only need to rename the categories
748
+ records.isetitem(i, records.iloc[:, i].cat.rename_categories(new_cats))
749
+
750
+ # remap special values
751
+ records = self._remap_str_special_values(records)
752
+
753
+ # convert data column to type float if needed
754
+ if records.iloc[:, -1].dtype != float:
755
+ records.isetitem(-1, records.iloc[:, -1].astype(float))
756
+
757
+ # reset columns
758
+ records.columns = generate_unique_labels(self.domain_names) + self._attributes
759
+
760
+ # set records
761
+ self.records = records
762
+
763
+ def toSparseCoo(self) -> Optional["coo_matrix"]:
764
+ """
765
+ Convert symbol to a sparse COOrdinate numpy.array format
766
+
767
+ Returns
768
+ -------
769
+ coo_matrix | None
770
+ """
771
+ from scipy.sparse import coo_matrix
772
+
773
+ if not self.isValid():
774
+ raise Exception(
775
+ "Cannot create sparse array (i.e., coo_matrix) because symbol "
776
+ "is invalid -- use `<symbol>.isValid(verbose=True)` to debug symbol state."
777
+ )
778
+ if self.domain_type == "regular":
779
+ if self.hasDomainViolations():
780
+ raise Exception(
781
+ "Cannot create sparse array because there are domain violations "
782
+ "(i.e., UELs in the symbol are not a subset of UELs contained in domain sets)."
783
+ )
784
+
785
+ if self.records is not None:
786
+ if self.is_scalar:
787
+ row = [0]
788
+ col = [0]
789
+ m = 1
790
+ n = 1
791
+
792
+ elif self.dimension == 1:
793
+ if self.domain_type == "regular":
794
+ col = (
795
+ self.records.iloc[:, 0]
796
+ .map(self.domain[0]._getUELCodes(0, ignore_unused=True))
797
+ .to_numpy(dtype=int)
798
+ )
799
+ else:
800
+ col = self.records.iloc[:, 0].cat.codes.to_numpy(dtype=int)
801
+
802
+ row = np.zeros(len(col), dtype=int)
803
+ m, *n = self.shape
804
+ assert n == []
805
+ n = m
806
+ m = 1
807
+
808
+ elif self.dimension == 2:
809
+ if self.domain_type == "regular":
810
+ row = (
811
+ self.records.iloc[:, 0]
812
+ .map(self.domain[0]._getUELCodes(0, ignore_unused=True))
813
+ .to_numpy(dtype=int)
814
+ )
815
+ col = (
816
+ self.records.iloc[:, 1]
817
+ .map(self.domain[1]._getUELCodes(0, ignore_unused=True))
818
+ .to_numpy(dtype=int)
819
+ )
820
+ else:
821
+ row = self.records.iloc[:, 0].cat.codes.to_numpy(dtype=int)
822
+ col = self.records.iloc[:, 1].cat.codes.to_numpy(dtype=int)
823
+
824
+ m, n = self.shape
825
+ else:
826
+ raise Exception(
827
+ "Sparse coo_matrix formats are only "
828
+ "available for data that has dimension <= 2"
829
+ )
830
+
831
+ return coo_matrix(
832
+ (self.records.iloc[:, -1].to_numpy(dtype=float), (row, col)),
833
+ shape=(m, n),
834
+ dtype=float,
835
+ )
836
+ else:
837
+ return None
838
+
839
+ def toDense(self) -> Union[np.ndarray, None]:
840
+ """
841
+ Convert symbol to a dense numpy.array format
842
+
843
+ Returns
844
+ -------
845
+ ndarray | None
846
+ A numpy array with symbol records, None if no records were assigned
847
+
848
+ Examples
849
+ --------
850
+ >>> m = gt.Container()
851
+ >>> j = gt.Set(m, "j", records=["new-york", "chicago", "topeka"])
852
+ >>> s = gt.Parameter(m, "s", [j], records=np.array([3,4,5]))
853
+ >>> print(s.toDense())
854
+ [3. 4. 5.]
855
+ """
856
+ if not self.isValid():
857
+ raise Exception(
858
+ "Cannot create dense array (i.e., matrix) format because symbol "
859
+ "is invalid -- use `<symbol>.isValid(verbose=True)` to debug symbol state."
860
+ )
861
+
862
+ if self.records is not None:
863
+ if self.is_scalar:
864
+ return self.records.to_numpy(dtype=float).reshape(self.shape)
865
+ else:
866
+ #
867
+ #
868
+ # checks
869
+ if self.domain_type == "regular":
870
+ if self.hasDomainViolations():
871
+ raise Exception(
872
+ "Cannot create dense array because there are domain violations "
873
+ "(i.e., UELs in the symbol are not a subset of UELs contained in domain sets)."
874
+ )
875
+
876
+ # check order of domain UELs in categorical and order of domain UELs in data
877
+ for symobj in self.domain:
878
+ data_cats = symobj.records.iloc[:, 0].unique().tolist()
879
+ cats = symobj.records.iloc[:, 0].cat.categories.tolist()
880
+
881
+ if data_cats != cats[: len(data_cats)]:
882
+ raise Exception(
883
+ f"`toDense` requires that UEL data order of domain set `{symobj.name}` must be "
884
+ "equal be equal to UEL category order (i.e., the order that set elements "
885
+ "appear in rows of the dataframe and the order set elements are specified by the categorical). "
886
+ "Users can efficiently reorder their domain set UELs to data order with "
887
+ "the `reorderUELs()` method (no arguments) -- preexisting unused categories "
888
+ "will be appended (maintaining their order)."
889
+ )
890
+ else:
891
+ # check order of domain UELs in categorical and order of domain UELs in data
892
+ for n in range(self.dimension):
893
+ # check if any invalid codes
894
+ if any(
895
+ code == -1 for code in self.records.iloc[:, n].cat.codes
896
+ ):
897
+ raise Exception(
898
+ f"Invalid category detected in dimension `{n}` (code == -1), "
899
+ "cannot create array until all categories are properly resolved"
900
+ )
901
+
902
+ data_cats = self.records.iloc[:, n].unique().tolist()
903
+ cats = self.records.iloc[:, n].cat.categories.tolist()
904
+
905
+ if data_cats != cats[: len(data_cats)]:
906
+ raise Exception(
907
+ f"`toDense` requires (for 'relaxed' symbols) that UEL data order must be "
908
+ "equal be equal to UEL category order (i.e., the order that set elements "
909
+ "appear in rows of the dataframe and the order set elements are specified by the categorical). "
910
+ "Users can efficiently reorder UELs to data order with "
911
+ "the `reorderUELs()` method (no arguments) -- preexisting unused categories "
912
+ "will be appended (maintaining their order)."
913
+ )
914
+
915
+ #
916
+ #
917
+ # create indexing scheme
918
+ if self.domain_type == "regular":
919
+ idx = [
920
+ self.records.iloc[:, n]
921
+ .map(domainobj._getUELCodes(0, ignore_unused=True))
922
+ .to_numpy(dtype=int)
923
+ for n, domainobj in enumerate(self.domain)
924
+ ]
925
+
926
+ else:
927
+ idx = [
928
+ self.records.iloc[:, n].cat.codes.to_numpy(dtype=int)
929
+ for n, domainobj in enumerate(self.domain)
930
+ ]
931
+
932
+ # fill the dense array
933
+ a = np.zeros(self.shape)
934
+ val = self.records.iloc[:, -1].to_numpy(dtype=float)
935
+ a[tuple(idx)] = val
936
+
937
+ return a
938
+ else:
939
+ return None
940
+
941
+ def _remap_str_special_values(self, records):
942
+ # convert str "eps", "na", & "undef" special value strings to float equivalents
943
+ if infer_dtype(records.iloc[:, -1]) not in [
944
+ "integer",
945
+ "floating",
946
+ "mixed-integer-float",
947
+ ]:
948
+ idx = records.iloc[:, -1].isin(EPS)
949
+ if idx.any():
950
+ records.loc[
951
+ records[idx].index,
952
+ records.columns[-1],
953
+ ] = SpecialValues.EPS
954
+
955
+ idx = records.iloc[:, -1].isin(UNDEF)
956
+ if idx.any():
957
+ records.loc[
958
+ records[idx].index,
959
+ records.columns[-1],
960
+ ] = SpecialValues.UNDEF
961
+
962
+ idx = records.iloc[:, -1].isin(NA)
963
+ if idx.any():
964
+ records.loc[
965
+ records[idx].index,
966
+ records.columns[-1],
967
+ ] = SpecialValues.NA
968
+
969
+ return records
970
+
971
+ def _filter_zero_records(self, records):
972
+ idx = (records.iloc[:, -1] == 0.0) & (~SpecialValues.isEps(records.iloc[:, -1]))
973
+ return records.loc[~idx, :].reset_index(drop=True)