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,436 @@
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 itertools
27
+ import random
28
+ import numpy as np
29
+ import pandas as pd
30
+ import gams.transfer._abcs as abcs
31
+ from typing import Sequence, Tuple, Union, List, Optional, TYPE_CHECKING
32
+
33
+ if TYPE_CHECKING:
34
+ from gams.transfer.syms import Set, Parameter, Variable, Equation
35
+
36
+
37
+ def check_all_same(iterable1: Sequence, iterable2: Sequence) -> bool:
38
+ """
39
+ Check if two sequences have the same elements in the same order.
40
+
41
+ Parameters
42
+ ----------
43
+ iterable1 : Sequence
44
+ The first sequence to compare.
45
+
46
+ iterable2 : Sequence
47
+ The second sequence to compare.
48
+
49
+ Returns
50
+ -------
51
+ bool
52
+ Returns True if both sequences have the same elements in the same order; otherwise, False.
53
+ """
54
+ if len(iterable1) != len(iterable2):
55
+ return False
56
+
57
+ all_same = True
58
+ for elem1, elem2 in zip(iterable1, iterable2):
59
+ if elem1 is not elem2:
60
+ return False
61
+ return all_same
62
+
63
+
64
+ def get_keys_and_values(
65
+ symobj: Union["Set", "Parameter", "Variable", "Equation"], mode: str
66
+ ) -> Tuple[np.ndarray, np.ndarray]:
67
+ """
68
+ Gets the keys and values of a specific symbol.
69
+
70
+ Parameters
71
+ ----------
72
+ symobj : Set | Parameter | Variable | Equation
73
+ The symbol to explore.
74
+ mode : str
75
+ The mode of keys and values; string or category.
76
+
77
+ Returns
78
+ -------
79
+ Tuple[ndarray, ndarray]
80
+ The tuple includes the symbol's keys and values.
81
+
82
+ Raises
83
+ ------
84
+ ValueError
85
+ If the given mode is niether 'string' nor 'category'.
86
+ """
87
+ if symobj.records is None:
88
+ if isinstance(symobj, abcs.ABCSet):
89
+ arrkeys = np.full(
90
+ (0, symobj.dimension), "", dtype=dtype_keys
91
+ ) # dtype_keys is not defined
92
+ arrvals = np.full((0, 1), "", dtype=object)
93
+ return (arrkeys, arrvals)
94
+ if isinstance(symobj, abcs.ABCParameter):
95
+ arrkeys = np.full(
96
+ (0, symobj.dimension), "", dtype=dtype_keys
97
+ ) # dtype_keys is not defined
98
+ arrvals = np.full((0, len(symobj._attributes)), "", dtype=np.float64)
99
+ return (arrkeys, arrvals)
100
+ if isinstance(symobj, (abcs.ABCVariable, abcs.ABCEquation)):
101
+ arrkeys = np.full(
102
+ (0, symobj.dimension), "", dtype=dtype_keys
103
+ ) # dtype_keys is not defined
104
+ arrvals = np.full((0, len(symobj._attributes)), "", dtype=np.float64)
105
+ return (arrkeys, arrvals)
106
+
107
+ else:
108
+ #
109
+ #
110
+ # get keys array
111
+ if mode == "string":
112
+ nrecs = symobj.number_records
113
+ if symobj.dimension == 0:
114
+ arrkeys = np.array([[]], dtype=object)
115
+ elif symobj.dimension == 1:
116
+ arrkeys = np.empty(nrecs, dtype=object)
117
+ arrkeys[:nrecs] = symobj.records[symobj.records.columns[0]]
118
+ arrkeys = arrkeys.reshape((nrecs, 1), order="F")
119
+ else:
120
+ arrkeys = np.empty(symobj.dimension * nrecs, dtype=object)
121
+ for i in range(symobj.dimension):
122
+ idx_start = i * nrecs
123
+ idx_end = i * nrecs + nrecs
124
+ arrkeys[idx_start:idx_end] = symobj.records[
125
+ symobj.records.columns[i]
126
+ ]
127
+
128
+ arrkeys = arrkeys.reshape((nrecs, symobj.dimension), order="F")
129
+
130
+ elif mode == "category":
131
+ nrecs = symobj.number_records
132
+ if symobj.dimension == 0:
133
+ arrkeys = np.array([[]], dtype=int)
134
+ elif symobj.dimension == 1:
135
+ arrkeys = np.empty(nrecs, dtype=int)
136
+ arrkeys[:nrecs] = symobj.records[symobj.records.columns[0]].cat.codes
137
+ arrkeys = arrkeys.reshape((nrecs, 1), order="F")
138
+ else:
139
+ arrkeys = np.empty(symobj.dimension * nrecs, dtype=int)
140
+ for i in range(symobj.dimension):
141
+ idx_start = i * nrecs
142
+ idx_end = i * nrecs + nrecs
143
+ arrkeys[idx_start:idx_end] = symobj.records[
144
+ symobj.records.columns[i]
145
+ ].cat.codes
146
+
147
+ arrkeys = arrkeys.reshape((nrecs, symobj.dimension), order="F")
148
+ else:
149
+ raise ValueError("Unrecognized write 'mode'.")
150
+ #
151
+ #
152
+ # get values array
153
+ if symobj.dimension == 0:
154
+ arrvals = symobj.records.to_numpy()
155
+ else:
156
+ if isinstance(symobj, (abcs.ABCSet, abcs.ABCParameter)):
157
+ arrvals = (
158
+ symobj.records[symobj.records.columns[-1]]
159
+ .to_numpy()
160
+ .reshape((-1, 1))
161
+ )
162
+ else:
163
+ arrvals = np.empty(len(symobj._attributes) * nrecs, dtype=np.float64)
164
+ for i in range(len(symobj._attributes)):
165
+ idx_start = i * nrecs
166
+ idx_end = i * nrecs + nrecs
167
+ arrvals[idx_start:idx_end] = symobj.records[
168
+ symobj.records.columns[i + symobj.dimension]
169
+ ].to_numpy()
170
+
171
+ arrvals = arrvals.reshape((nrecs, len(symobj._attributes)), order="F")
172
+
173
+ return (arrkeys, arrvals)
174
+
175
+
176
+ def convert_to_categoricals_str(
177
+ arrkeys: np.ndarray, arrvals: np.ndarray, all_uels: Sequence
178
+ ) -> pd.DataFrame:
179
+ """
180
+ This function takes two lists, `arrkeys` and `arrvals`, and converts them into a pandas
181
+ DataFrame.
182
+
183
+ Parameters
184
+ ----------
185
+ arrkeys : ndarray
186
+ An array of keys (e.g., labels or domain values).
187
+
188
+ arrvals : ndarray
189
+ An array of values corresponding to the keys.
190
+
191
+ all_uels : Sequence
192
+ A sequence of all unique elements (categories) that should be used to define
193
+ the categorical data.
194
+
195
+ Returns
196
+ -------
197
+ pd.DataFrame
198
+ A pandas DataFrame with categorical columns based on `arrkeys` and `arrvals`.
199
+
200
+ Notes
201
+ -----
202
+ - This function assumes that the lengths of `arrkeys` and `arrvals` are compatible.
203
+ """
204
+ has_domains = arrkeys.size > 0
205
+ has_values = arrvals.size > 0
206
+
207
+ dfs = []
208
+ if has_domains:
209
+ dfs.append(pd.DataFrame(arrkeys))
210
+
211
+ if has_values:
212
+ dfs.append(pd.DataFrame(arrvals))
213
+
214
+ if has_domains and has_values:
215
+ df = pd.concat(dfs, axis=1, copy=False)
216
+ df.columns = pd.RangeIndex(start=0, stop=len(df.columns))
217
+ elif has_domains or has_values:
218
+ df = dfs[0]
219
+ df.columns = pd.RangeIndex(start=0, stop=len(df.columns))
220
+ else:
221
+ df = None
222
+
223
+ if has_domains:
224
+ rk, ck = arrkeys.shape
225
+ for i in range(ck):
226
+ dtype = pd.CategoricalDtype(categories=all_uels, ordered=True)
227
+ df.isetitem(
228
+ i, pd.Categorical(values=df[i], dtype=dtype).remove_unused_categories()
229
+ )
230
+
231
+ return df
232
+
233
+
234
+ def convert_to_categoricals_cat(
235
+ arrkeys: np.ndarray, arrvals: np.ndarray, unique_uels: Sequence
236
+ ) -> pd.DataFrame:
237
+ has_domains = arrkeys.size > 0
238
+ has_values = arrvals.size > 0
239
+
240
+ dfs = []
241
+ if has_domains:
242
+ dfs.append(pd.DataFrame(arrkeys))
243
+
244
+ if has_values:
245
+ dfs.append(pd.DataFrame(arrvals))
246
+
247
+ if has_domains and has_values:
248
+ df = pd.concat(dfs, axis=1, copy=False)
249
+ df.columns = pd.RangeIndex(start=0, stop=len(df.columns))
250
+ elif has_domains or has_values:
251
+ df = dfs[0]
252
+ df.columns = pd.RangeIndex(start=0, stop=len(df.columns))
253
+ else:
254
+ df = None
255
+
256
+ if has_domains:
257
+ _, ck = arrkeys.shape
258
+ for i in range(ck):
259
+ dtype = pd.CategoricalDtype(categories=unique_uels[i], ordered=True)
260
+ df.isetitem(i, pd.Categorical.from_codes(codes=df[i], dtype=dtype))
261
+
262
+ return df
263
+
264
+
265
+ def generate_unique_labels(labels: Union[list, str]) -> List[str]:
266
+ """
267
+ Generate unique labels from a list of labels.
268
+
269
+ Parameters
270
+ ----------
271
+ labels : list | str
272
+ A list of labels to be processed. This can be a single label or a list of labels.
273
+
274
+ Returns
275
+ -------
276
+ List[str]
277
+ A list of labels with uniqueness enforced. If the input list contains duplicates, this
278
+ function appends numeric suffixes to make the labels unique. If the input is not a list,
279
+ it is converted to a single-element list.
280
+ """
281
+ if not isinstance(labels, list):
282
+ labels = [labels]
283
+
284
+ # default domain labels
285
+ labels = [i if i != "*" else "uni" for i in labels]
286
+
287
+ # make unique
288
+ is_unique = False
289
+ if len(labels) == len(set(labels)):
290
+ is_unique = True
291
+
292
+ if not is_unique:
293
+ labels = [f"{i}_{n}" for n, i in enumerate(labels)]
294
+
295
+ return labels
296
+
297
+
298
+ def cartesian_product(*arrays: Tuple[np.ndarray]) -> np.ndarray:
299
+ """
300
+ Calculate the Cartesian product of multiple input arrays.
301
+
302
+ Given multiple input arrays, this function computes the Cartesian product of these arrays.
303
+ The Cartesian product is an array containing all possible combinations of elements from
304
+ the input arrays, where each element in the result is a tuple representing one combination.
305
+
306
+ Parameters
307
+ ----------
308
+ *arrays : Tuple[ndarray]
309
+ Multiple input arrays for which the Cartesian product is calculated.
310
+
311
+ Returns
312
+ -------
313
+ ndarray
314
+ A NumPy array containing the Cartesian product of the input arrays. Each row
315
+ represents a unique combination, and the columns represent the elements from the
316
+ input arrays.
317
+
318
+ Notes
319
+ -----
320
+ - The input arrays should be of compatible data types.
321
+ - The number of input arrays and their shapes may vary, and the function works for any
322
+ number of input arrays.
323
+
324
+ Examples
325
+ -------_
326
+ >>> import numpy as np
327
+ >>> arr1 = np.array([1, 2])
328
+ >>> arr2 = np.array(['A', 'B'])
329
+ >>> arr3 = np.array([0.1, 0.2])
330
+ >>> result = cartesian_product(arr1, arr2, arr3)
331
+ >>> print(result)
332
+ array([[1, 'A', 0.1],
333
+ [1, 'A', 0.2],
334
+ [1, 'B', 0.1],
335
+ [1, 'B', 0.2],
336
+ [2, 'A', 0.1],
337
+ [2, 'A', 0.2],
338
+ [2, 'B', 0.1],
339
+ [2, 'B', 0.2]], dtype=object)
340
+
341
+ """
342
+
343
+ la = len(arrays)
344
+ dtype = np.result_type(*arrays)
345
+ arr = np.empty((la, *map(len, arrays)), dtype=dtype)
346
+ idx = slice(None), *itertools.repeat(None, la)
347
+ for i, a in enumerate(arrays):
348
+ arr[i, ...] = a[idx[: la - i]]
349
+
350
+ return arr.reshape(la, -1).T
351
+
352
+
353
+ def choice_no_replace(
354
+ choose_from: int,
355
+ n_choose: int,
356
+ seed: Optional[int] = None,
357
+ ) -> np.ndarray:
358
+ """
359
+ Randomly select 'n_choose' unique items from a pool of 'choose_from' items without replacement.
360
+
361
+ This function allows you to randomly choose a specified number of unique items from a pool of items
362
+ defined by 'choose_from'. It ensures that the selected items are unique, meaning each item is chosen
363
+ only once.
364
+
365
+ Parameters
366
+ ----------
367
+ choose_from : int
368
+ The total number of items to choose from.
369
+
370
+ n_choose : int
371
+ The number of items to select from the pool.
372
+
373
+ seed : int, optional
374
+ An optional seed for the random number generator. If provided, it should be an integer or None.
375
+ If 'seed' is None, the random selection will not be reproducible.
376
+
377
+ Returns
378
+ -------
379
+ ndarray
380
+ An array containing the selected items. The array will have a length of 'n_choose', and the
381
+ items will be in random order.
382
+
383
+ Raises
384
+ ------
385
+ TypeError
386
+ If 'seed' is not an integer or None.
387
+
388
+ Exception
389
+ If the 'density' of selection is out of bounds, which must be in the interval [0, 1].
390
+
391
+ Notes
392
+ -----
393
+ - For high-density selections (density > 0.08), NumPy is used for better performance.
394
+ - For low-density selections (density <= 0.08), the built-in 'random' module is used for speed.
395
+
396
+ Examples
397
+ --------
398
+ >>> choice_no_replace(100, 10, seed=42)
399
+ array([8, 9, 19, 41, 60, 68, 71, 82, 94, 96])
400
+ """
401
+
402
+ if not isinstance(seed, (int, type(None))):
403
+ raise TypeError("Argument 'seed' must be type int or NoneType")
404
+
405
+ if not isinstance(choose_from, int):
406
+ choose_from = int(choose_from)
407
+
408
+ if not isinstance(n_choose, int):
409
+ n_choose = int(n_choose)
410
+
411
+ density = n_choose / choose_from
412
+
413
+ try:
414
+ if density == 1:
415
+ return np.arange(choose_from, dtype=int)
416
+
417
+ # numpy is faster as density grows
418
+ if 0.08 < density < 1:
419
+ rng = np.random.default_rng(seed)
420
+ idx = rng.choice(
421
+ np.arange(choose_from, dtype=int), replace=False, size=(n_choose,)
422
+ )
423
+
424
+ # random.shuffle is much much faster at low density
425
+ elif density <= 0.08:
426
+ random.seed(seed)
427
+ idx = np.array(random.sample(range(choose_from), n_choose), dtype=int)
428
+ else:
429
+ raise Exception(
430
+ "Argument 'density' is out of bounds, must be on the interval [0,1]."
431
+ )
432
+
433
+ return np.sort(idx)
434
+
435
+ except Exception as err:
436
+ raise err
@@ -0,0 +1,124 @@
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 collections import UserDict
27
+ from typing import Any, Optional, TYPE_CHECKING
28
+ from _collections_abc import dict_items, dict_keys, dict_values
29
+
30
+
31
+ class CasePreservingDict(UserDict):
32
+ def __init__(self, *args, **kwargs):
33
+ self._casefolded_key_map = {}
34
+ super().__init__(*args, **kwargs)
35
+
36
+ def __contains__(self, key):
37
+ return key.casefold() in self._casefolded_key_map
38
+
39
+ def __delitem__(self, key):
40
+ del self.data[self._casefolded_key_map[key.casefold()]]
41
+ del self._casefolded_key_map[key.casefold()]
42
+
43
+ def __setitem__(self, key, item):
44
+ try:
45
+ key_cf = key.casefold()
46
+ except:
47
+ raise TypeError("CasePreservingDict will only accept type str as keys")
48
+
49
+ if key_cf not in self._casefolded_key_map:
50
+ self.data[key] = item
51
+ self._casefolded_key_map[key_cf] = key
52
+ else:
53
+ self.data[self._casefolded_key_map[key_cf]] = item
54
+
55
+ def _repr_pretty_(self, p, cycle):
56
+ if cycle:
57
+ p.pretty(self.data)
58
+ else:
59
+ p.pretty(self.data)
60
+
61
+ def __getitem__(self, key):
62
+ return self.data[self._casefolded_key_map[key.casefold()]]
63
+
64
+ def pop(self, key: str) -> Any:
65
+ a = self.data.pop(self._casefolded_key_map[key.casefold()])
66
+ del self._casefolded_key_map[key.casefold()]
67
+ return a
68
+
69
+ def get(self, key: str, value: Optional[Any] = None) -> Any:
70
+ try:
71
+ return self.data.get(self._casefolded_key_map[key.casefold()])
72
+ except:
73
+ return value
74
+
75
+ def copy(self) -> "CasePreservingDict":
76
+ """
77
+ Create a deep copy of the CasePreservingDict.
78
+
79
+ Returns
80
+ -------
81
+ CasePreservingDict
82
+ A deep copy of the CasePreservingDict.
83
+ """
84
+ import copy
85
+
86
+ return copy.deepcopy(self)
87
+
88
+ def setdefault(self, key: str, default: Any) -> Any:
89
+ """
90
+ Set the default value for a key if it does not exist (case-insensitive).
91
+
92
+ If the key already exists in the dictionary (case-insensitive), this function does not modify the existing value.
93
+
94
+ Parameters
95
+ ----------
96
+ key : str
97
+ The key to set the default value for.
98
+
99
+ default : Any
100
+ The default value to associate with the key if it does not exist.
101
+
102
+ Returns
103
+ -------
104
+ Any
105
+ The value associated with the key (case-insensitive) or the default value if the key was not found.
106
+ """
107
+ key_cf = key.casefold()
108
+ if key_cf not in self._casefolded_key_map.keys():
109
+ self.__setitem__(key, default)
110
+ return default
111
+ else:
112
+ return self.data[self._casefolded_key_map[key_cf]]
113
+
114
+ def popitem(self) -> tuple:
115
+ return self.data.popitem()
116
+
117
+ def items(self) -> dict_items:
118
+ return self.data.items()
119
+
120
+ def keys(self) -> dict_keys:
121
+ return self.data.keys()
122
+
123
+ def values(self) -> dict_values:
124
+ return self.data.values()