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,626 @@
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 pandas as pd
27
+ import numpy as np
28
+ from gams.transfer._abcs import ABCAlias, AnyContainerSymbol
29
+ from gams.transfer._internals import SpecialValues
30
+ import typing
31
+ from typing import Union, Optional
32
+
33
+ if typing.TYPE_CHECKING:
34
+ from gams.transfer.syms.container_syms._set import Set
35
+ from gams.transfer.syms.container_syms._alias import Alias
36
+ from gams.transfer.syms.container_syms._parameter import Parameter
37
+ from gams.transfer.syms.container_syms._variable import Variable
38
+
39
+
40
+ class EqualsBase:
41
+ @typing.no_type_check
42
+ def equals(self, other, check_uels, check_meta_data, verbose):
43
+ #
44
+ # ARG: self
45
+ if not self.isValid():
46
+ raise Exception(
47
+ f"Cannot compare objects because `{self.name}` is not a valid symbol object"
48
+ "Use `<symbol>.isValid(verbose=True)` to debug further."
49
+ )
50
+
51
+ #
52
+ # ARG: other
53
+ if not isinstance(other, AnyContainerSymbol):
54
+ raise TypeError("Argument 'other' must be a GAMS Symbol object")
55
+
56
+ if not other.isValid():
57
+ raise Exception(
58
+ f"Cannot compare objects because `{other.name}` is not a valid symbol object"
59
+ "Use `<symbol>.isValid(verbose=True)` to debug further."
60
+ )
61
+
62
+ # adjustments
63
+ if isinstance(other, ABCAlias):
64
+ other = other.alias_with
65
+
66
+ #
67
+ # ARG: self & other
68
+ if not isinstance(self, type(other)):
69
+ raise TypeError(
70
+ f"Symbol are not of the same type (`{type(self)}` != `{type(other)}`)"
71
+ )
72
+
73
+ # test for equal variable and equation types
74
+ if getattr(self, "type", None) != getattr(other, "type", None):
75
+ raise Exception(
76
+ f"Symbol types do not match (`{self.type}` != `{other.type}`)"
77
+ )
78
+
79
+ #
80
+ # ARG: check_uels
81
+ if not isinstance(check_uels, bool):
82
+ raise TypeError("Argument 'check_uels' must be type bool")
83
+
84
+ #
85
+ # ARG: check_meta_data
86
+ if not isinstance(check_meta_data, bool):
87
+ raise TypeError("Argument 'check_meta_data' must be type bool")
88
+
89
+ #
90
+ # ARG: verbose
91
+ if not isinstance(verbose, bool):
92
+ raise TypeError("Argument 'verbose' must be type bool")
93
+
94
+ return other
95
+
96
+ def _assert_symbol_attributes(
97
+ self,
98
+ other,
99
+ check_uels,
100
+ check_meta_data,
101
+ ):
102
+ #
103
+ # Mandatory checks
104
+ if self.dimension != other.dimension:
105
+ raise Exception(
106
+ f"Symbol dimensions do not match (`{self.dimension}` != `{other.dimension}`)"
107
+ )
108
+
109
+ if self.domain_type != other.domain_type:
110
+ raise Exception(
111
+ f"Symbol domain_types do not match (`{self.domain_type}` != `{other.domain_type}`)"
112
+ )
113
+
114
+ if self.records is not None and other.records is not None:
115
+ if self.number_records != other.number_records:
116
+ raise Exception(
117
+ "Symbols do not have the same number of records "
118
+ f"(`{self.number_records}` != `{other.number_records}`)"
119
+ )
120
+
121
+ if not isinstance(self.records, type(other.records)):
122
+ raise Exception(
123
+ f"Symbol records type do not match (`{type(self.records)}` != `{type(other.records)}`)"
124
+ )
125
+
126
+ # check domains even if symbols are in different containers
127
+ different_containers = True if self.container is not other.container else False
128
+ for n, (selfdom, otherdom) in enumerate(zip(self.domain, other.domain)):
129
+ if type(selfdom) is not type(otherdom):
130
+ raise Exception(
131
+ f"Domain symbols in dimension {n} (zero-indexed) are not the same type (i.e., '{selfdom.name}' "
132
+ f"is {type(selfdom)} does not match '{otherdom.name}' which is {type(otherdom)})"
133
+ )
134
+
135
+ if not different_containers:
136
+ if selfdom is not otherdom:
137
+ raise Exception(
138
+ f"Domain symbols in dimension {n} (zero-indexed) are not the same object (i.e., '{selfdom.name}' is not '{otherdom.name}')"
139
+ )
140
+ else:
141
+ if not isinstance(selfdom, str):
142
+ if selfdom.name != otherdom.name:
143
+ raise Exception(
144
+ f"Domain symbols in dimension {n} (zero-indexed) do not have the same name (i.e., '{selfdom.name}' != '{otherdom.name}')"
145
+ )
146
+
147
+ try:
148
+ selfdom.equals(otherdom, verbose=True)
149
+ except Exception as err:
150
+ raise Exception(
151
+ f"Domain symbols '{selfdom.name}' are not equal . Reason: {err}"
152
+ ) from err
153
+
154
+ #
155
+ # Check metadata (optional)
156
+ if check_meta_data:
157
+ if self.name != other.name:
158
+ raise Exception(
159
+ f"Symbol names do not match (`{self.name}` != `{other.name}`)"
160
+ )
161
+
162
+ if self.description != other.description:
163
+ raise Exception(
164
+ f"Symbol descriptions do not match (`{self.description}` != `{other.description}`)"
165
+ )
166
+
167
+ # Check UELs (optional)
168
+ if check_uels:
169
+ if self.records is not None and other.records is not None:
170
+ left_uels = self.getUELs()
171
+ right_uels = other.getUELs()
172
+
173
+ if left_uels != right_uels:
174
+ raise Exception(
175
+ "Symbol UEL ordering does not match \n\n"
176
+ f"[self]: {left_uels} \n"
177
+ f"[other]: {right_uels} \n"
178
+ )
179
+
180
+ def _merge_records(self, other):
181
+ merged = pd.DataFrame()
182
+ if self.records is not None and other.records is not None:
183
+ if not self.records.empty and not other.records.empty:
184
+ merged = self.records.merge(
185
+ other.records,
186
+ how="outer",
187
+ left_on=self.domain_labels,
188
+ right_on=other.domain_labels,
189
+ indicator=True,
190
+ )
191
+
192
+ return merged
193
+
194
+ def _assert_scalar_values(self, other, columns, rtol, atol):
195
+ for attr in columns:
196
+ for svlabel, SV in zip(
197
+ ["EPS", "NA", "UNDEF"],
198
+ [
199
+ SpecialValues.isEps,
200
+ SpecialValues.isNA,
201
+ SpecialValues.isUndef,
202
+ ],
203
+ ):
204
+ self_is_special = SV(self.records[attr])
205
+ other_is_special = SV(other.records[attr])
206
+
207
+ if self_is_special != other_is_special:
208
+ raise Exception(
209
+ f"Symbol records with `{svlabel}` special values "
210
+ f"do not match in the `{attr}` column."
211
+ )
212
+
213
+ if self_is_special == False and other_is_special == False:
214
+ if not np.isclose(
215
+ self.records[attr],
216
+ other.records[attr],
217
+ rtol=rtol,
218
+ atol=atol,
219
+ ):
220
+ raise Exception(
221
+ f"Symbol records contain numeric difference in the `{attr}` attribute "
222
+ f"that are outside the specified tolerances (rtol={rtol}, atol={atol})"
223
+ )
224
+
225
+ @typing.no_type_check
226
+ def _assert_symbol_domains(self, merged):
227
+ if set(merged["_merge"]) != {"both"}:
228
+ self_only_recs = merged[merged["_merge"].isin({"left_only"})].head()
229
+ other_only_recs = merged[merged["_merge"].isin({"right_only"})].head()
230
+
231
+ if self_only_recs.empty:
232
+ self_only_recs = "All matched OK"
233
+ else:
234
+ self_only_recs = list(
235
+ self_only_recs[self_only_recs.columns[: self.dimension]].itertuples(
236
+ index=False, name=None
237
+ )
238
+ )
239
+
240
+ if other_only_recs.empty:
241
+ other_only_recs = "All matched OK"
242
+ else:
243
+ other_only_recs = list(
244
+ other_only_recs[
245
+ other_only_recs.columns[: other.dimension]
246
+ ].itertuples(index=False, name=None)
247
+ )
248
+
249
+ raise Exception(
250
+ "Symbol records do not match. First five unmatched domains: \n\n"
251
+ f"left_only : {self_only_recs} \n"
252
+ f"right_only: {other_only_recs} \n"
253
+ )
254
+
255
+
256
+ class EqualsSetMixin(EqualsBase):
257
+ @typing.no_type_check
258
+ def equals(
259
+ self,
260
+ other: Union["Set", "Alias"],
261
+ check_uels: bool = True,
262
+ check_element_text: bool = True,
263
+ check_meta_data: bool = True,
264
+ verbose: bool = False,
265
+ ) -> bool:
266
+ """
267
+ Used to compare the symbol to another symbol
268
+
269
+ Parameters
270
+ ----------
271
+ other : Set | Alias
272
+ Other Symbol to compare with
273
+ check_uels : bool, optional
274
+ If True, check both used and unused UELs and confirm same order, otherwise only check used UELs in data and do not check UEL order, by default True
275
+ check_element_text : bool, optional
276
+ If True, check that all set elements have the same descriptive element text, otherwise skip, by default True
277
+ check_meta_data : bool, optional
278
+ If True, check that symbol name and description are the same, otherwise skip, by default True
279
+ verbose : bool, optional
280
+ If True, will return an exception from the asserter describing the nature of the difference, by default False
281
+
282
+ Returns
283
+ -------
284
+ bool
285
+ True if symbols are equal, False otherwise
286
+ """
287
+
288
+ # check & set
289
+ other = super().equals(other, check_uels, check_meta_data, verbose)
290
+
291
+ # check is_singleton
292
+ if self.is_singleton != other.is_singleton:
293
+ raise Exception("Symbols do not have matching 'is_singleton' state")
294
+
295
+ # extension to check check_element_text
296
+ # ARG: check_element_text
297
+ if not isinstance(check_element_text, bool):
298
+ raise TypeError("Argument 'check_element_text' must be type bool")
299
+
300
+ try:
301
+ # check symbol attributes (not records)
302
+ super()._assert_symbol_attributes(other, check_uels, check_meta_data)
303
+
304
+ # merge records
305
+ merged = super()._merge_records(other)
306
+
307
+ # check symbol domain records
308
+ self._assert_symbol_domains(merged, check_element_text)
309
+
310
+ return True
311
+ except Exception as err:
312
+ if verbose:
313
+ raise err
314
+ else:
315
+ return False
316
+
317
+ @typing.no_type_check
318
+ def _assert_symbol_domains(self, merged, check_element_text):
319
+ if not merged.empty:
320
+ # check domains
321
+ super()._assert_symbol_domains(merged)
322
+
323
+ # extension to check element text
324
+ if check_element_text:
325
+ merged["_element_text"] = (
326
+ merged["element_text_x"] != merged["element_text_y"]
327
+ )
328
+
329
+ recs = merged[merged["_element_text"]].head()
330
+
331
+ if not recs.empty:
332
+ self_only_recs = list(
333
+ recs[
334
+ list(recs.columns[: self.dimension]) + ["element_text_x"]
335
+ ].itertuples(index=False, name=None)
336
+ )
337
+
338
+ other_only_recs = list(
339
+ recs[
340
+ list(recs.columns[: self.dimension]) + ["element_text_y"]
341
+ ].itertuples(index=False, name=None)
342
+ )
343
+
344
+ raise Exception(
345
+ "Symbol element_text does not match. First five unmatched domains: \n\n"
346
+ f"left_only : {self_only_recs} \n"
347
+ f"right_only: {other_only_recs} \n"
348
+ )
349
+
350
+
351
+ class EqualsParameterMixin(EqualsBase):
352
+ @typing.no_type_check
353
+ def equals(
354
+ self,
355
+ other: "Parameter",
356
+ check_uels: bool = True,
357
+ check_meta_data: bool = True,
358
+ rtol: Optional[Union[int, float]] = None,
359
+ atol: Optional[Union[int, float]] = None,
360
+ verbose: bool = False,
361
+ ) -> bool:
362
+ """
363
+ Used to compare the symbol to another symbol
364
+
365
+ Parameters
366
+ ----------
367
+ other : Parameter
368
+ Other Symbol to compare with
369
+ check_uels : bool, optional
370
+ If True, check both used and unused UELs and confirm same order, otherwise only check used UELs in data and do not check UEL order. by default True
371
+ check_meta_data : bool, optional
372
+ If True, check that symbol name and description are the same, otherwise skip. by default True
373
+ rtol : int | float, optional
374
+ relative tolerance, by default None
375
+ atol : int | float, optional
376
+ absolute tolerance, by default None
377
+ verbose : bool, optional
378
+ If True, will return an exception from the asserter describing the nature of the difference. by default False
379
+
380
+ Returns
381
+ -------
382
+ bool
383
+ True if symbols are equal, False otherwise
384
+ """
385
+
386
+ # check & set
387
+ other = super().equals(other, check_uels, check_meta_data, verbose)
388
+
389
+ # set
390
+ columns = self._attributes
391
+
392
+ # extension to check rtol, atol
393
+ # ARG: rtol & atol
394
+ if not isinstance(rtol, (type(None), int, float)):
395
+ raise ValueError(
396
+ "Argument 'rtol' (relative tolerance) must be "
397
+ f"numeric (int, float) or None. User passed: {type(rtol)}."
398
+ )
399
+
400
+ if not isinstance(atol, (type(None), int, float)):
401
+ raise ValueError(
402
+ "Argument 'atol' (relative tolerance) must be "
403
+ f"numeric (int, float) or None. User passed: {type(rtol)}."
404
+ )
405
+
406
+ # set defaults
407
+ if rtol is None:
408
+ rtol = 0.0
409
+
410
+ if atol is None:
411
+ atol = 0.0
412
+
413
+ try:
414
+ # check symbol attributes (not records)
415
+ super()._assert_symbol_attributes(other, check_uels, check_meta_data)
416
+
417
+ if self.dimension == 0:
418
+ super()._assert_scalar_values(other, columns, rtol, atol)
419
+
420
+ else:
421
+ # merge records
422
+ merged = super()._merge_records(other)
423
+
424
+ # check domains
425
+ super()._assert_symbol_domains(merged)
426
+
427
+ # check values
428
+ self._assert_symbol_values(merged, columns, rtol, atol)
429
+
430
+ return True
431
+ except Exception as err:
432
+ if verbose:
433
+ raise err
434
+ else:
435
+ return False
436
+
437
+ def _assert_symbol_values(self, merged, columns, rtol, atol):
438
+ if not merged.empty:
439
+ for attr in columns:
440
+ small_merged = merged[
441
+ list(merged.columns[: self.dimension]) + [f"{attr}_x", f"{attr}_y"]
442
+ ].copy()
443
+
444
+ for svlabel, SV in zip(
445
+ ["EPS", "NA", "UNDEF"],
446
+ [
447
+ SpecialValues.isEps,
448
+ SpecialValues.isNA,
449
+ SpecialValues.isUndef,
450
+ ],
451
+ ):
452
+ self_idx = SV(small_merged[f"{attr}_x"])
453
+ other_idx = SV(small_merged[f"{attr}_y"])
454
+
455
+ if any(self_idx != other_idx):
456
+ raise Exception(
457
+ f"Symbol records with `{svlabel}` special values "
458
+ f"do not match in the `{attr}` column."
459
+ )
460
+
461
+ #
462
+ # drop special values if all indices match
463
+ small_merged.drop(index=small_merged[self_idx].index, inplace=True)
464
+
465
+ #
466
+ # check attr values (subject to tolerances)
467
+ small_merged["isclose"] = np.isclose(
468
+ small_merged[f"{attr}_x"],
469
+ small_merged[f"{attr}_y"],
470
+ rtol=rtol,
471
+ atol=atol,
472
+ )
473
+
474
+ if any(~small_merged["isclose"]):
475
+ raise Exception(
476
+ f"Symbol records contain numeric difference in the `{attr}` attribute "
477
+ f"that are outside the specified tolerances (rtol={rtol}, atol={atol})"
478
+ )
479
+
480
+
481
+ class EqualsVariableMixin(EqualsBase):
482
+ @typing.no_type_check
483
+ def equals(
484
+ self,
485
+ other: "Variable",
486
+ columns: str = None,
487
+ check_uels: bool = True,
488
+ check_meta_data: bool = True,
489
+ rtol: Optional[Union[int, float]] = None,
490
+ atol: Optional[Union[int, float]] = None,
491
+ verbose: bool = False,
492
+ ) -> bool:
493
+ """
494
+ Used to compare the symbol to another symbol
495
+
496
+ Parameters
497
+ ----------
498
+ other : Variable
499
+ _description_
500
+ columns : str, optional
501
+ allows the user to numerically compare only specified variable attributes, by default None; compare all
502
+ check_uels : bool, optional
503
+ If True, check both used and unused UELs and confirm same order, otherwise only check used UELs in data and do not check UEL order. by default True
504
+ check_meta_data : bool, optional
505
+ If True, check that symbol name and description are the same, otherwise skip. by default True
506
+ rtol : int | float, optional
507
+ relative tolerance, by default None
508
+ atol : int | float, optional
509
+ absolute tolerance, by default None
510
+ verbose : bool, optional
511
+ If True, will return an exception from the asserter describing the nature of the difference. by default False
512
+
513
+ Returns
514
+ -------
515
+ bool
516
+ True if symbols are equal, False otherwise
517
+ """
518
+ # check & set
519
+ other = super().equals(other, check_uels, check_meta_data, verbose)
520
+
521
+ # extension to test columns
522
+ # ARG: columns
523
+ if not isinstance(columns, (str, list, type(None))):
524
+ raise TypeError(f"Argument 'columns' must be type str, list or NoneType")
525
+
526
+ if isinstance(columns, str):
527
+ columns = [columns]
528
+
529
+ if columns is None:
530
+ columns = self._attributes
531
+
532
+ if any(i not in self._attributes for i in columns):
533
+ raise ValueError(
534
+ f"Argument 'columns' can only contain the following symbol attributes: {self._attributes}"
535
+ )
536
+
537
+ # extension to check rtol, atol
538
+ # ARG: rtol & atol
539
+ if not isinstance(rtol, (type(None), int, float)):
540
+ raise ValueError(
541
+ "Argument 'rtol' (relative tolerance) must be "
542
+ f"numeric (int, float) or None. User passed: {type(rtol)}."
543
+ )
544
+
545
+ if not isinstance(atol, (type(None), int, float)):
546
+ raise ValueError(
547
+ "Argument 'atol' (relative tolerance) must be "
548
+ f"numeric (int, float) or None. User passed: {type(rtol)}."
549
+ )
550
+
551
+ # set defaults
552
+ if rtol is None:
553
+ rtol = 0.0
554
+
555
+ if atol is None:
556
+ atol = 0.0
557
+
558
+ try:
559
+ # check symbol attributes (not records)
560
+ super()._assert_symbol_attributes(other, check_uels, check_meta_data)
561
+
562
+ if self.dimension == 0:
563
+ super()._assert_scalar_values(other, columns, rtol, atol)
564
+
565
+ else:
566
+ # merge records
567
+ merged = super()._merge_records(other)
568
+
569
+ # check domains
570
+ super()._assert_symbol_domains(merged)
571
+
572
+ # check values
573
+ self._assert_symbol_values(merged, columns, rtol, atol)
574
+
575
+ return True
576
+ except Exception as err:
577
+ if verbose:
578
+ raise err
579
+ else:
580
+ return False
581
+
582
+ def _assert_symbol_values(self, merged, columns, rtol, atol):
583
+ if not merged.empty:
584
+ for attr in columns:
585
+ small_merged = merged[
586
+ list(merged.columns[: self.dimension]) + [f"{attr}_x", f"{attr}_y"]
587
+ ].copy()
588
+
589
+ for svlabel, SV in zip(
590
+ ["EPS", "NA", "UNDEF"],
591
+ [
592
+ SpecialValues.isEps,
593
+ SpecialValues.isNA,
594
+ SpecialValues.isUndef,
595
+ ],
596
+ ):
597
+ self_idx = SV(small_merged[f"{attr}_x"])
598
+ other_idx = SV(small_merged[f"{attr}_y"])
599
+
600
+ if any(self_idx != other_idx):
601
+ raise Exception(
602
+ f"Symbol records with `{svlabel}` special values "
603
+ f"do not match in the `{attr}` column."
604
+ )
605
+
606
+ #
607
+ # drop special values if all indices match
608
+ small_merged.drop(index=small_merged[self_idx].index, inplace=True)
609
+
610
+ #
611
+ # check attr values (subject to tolerances)
612
+ isclose = np.isclose(
613
+ small_merged[f"{attr}_x"],
614
+ small_merged[f"{attr}_y"],
615
+ rtol=rtol,
616
+ atol=atol,
617
+ )
618
+
619
+ if any(~isclose):
620
+ raise Exception(
621
+ f"Symbol records contain numeric difference in the `{attr}` attribute "
622
+ f"that are outside the specified tolerances (rtol={rtol}, atol={atol})"
623
+ )
624
+
625
+
626
+ class EqualsEquationMixin(EqualsVariableMixin): ...