gamspy 1.18.3__py3-none-any.whl → 1.19.0__py3-none-any.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 (89) hide show
  1. gamspy/__init__.py +86 -98
  2. gamspy/__main__.py +6 -6
  3. gamspy/_algebra/__init__.py +13 -13
  4. gamspy/_algebra/condition.py +290 -194
  5. gamspy/_algebra/domain.py +103 -93
  6. gamspy/_algebra/expression.py +820 -799
  7. gamspy/_algebra/number.py +79 -70
  8. gamspy/_algebra/operable.py +185 -185
  9. gamspy/_algebra/operation.py +948 -845
  10. gamspy/_backend/backend.py +313 -311
  11. gamspy/_backend/engine.py +960 -960
  12. gamspy/_backend/local.py +124 -124
  13. gamspy/_backend/neos.py +567 -567
  14. gamspy/_cli/__init__.py +1 -1
  15. gamspy/_cli/cli.py +64 -64
  16. gamspy/_cli/gdx.py +377 -377
  17. gamspy/_cli/install.py +375 -372
  18. gamspy/_cli/list.py +94 -94
  19. gamspy/_cli/mps2gms.py +128 -128
  20. gamspy/_cli/probe.py +52 -52
  21. gamspy/_cli/retrieve.py +79 -79
  22. gamspy/_cli/run.py +158 -158
  23. gamspy/_cli/show.py +246 -255
  24. gamspy/_cli/uninstall.py +165 -165
  25. gamspy/_cli/util.py +94 -94
  26. gamspy/_communication.py +215 -215
  27. gamspy/_config.py +132 -132
  28. gamspy/_container.py +1694 -1452
  29. gamspy/_convert.py +720 -720
  30. gamspy/_database.py +271 -271
  31. gamspy/_extrinsic.py +181 -181
  32. gamspy/_miro.py +356 -352
  33. gamspy/_model.py +1803 -1615
  34. gamspy/_model_instance.py +701 -701
  35. gamspy/_options.py +780 -700
  36. gamspy/_serialization.py +156 -144
  37. gamspy/_symbols/__init__.py +17 -17
  38. gamspy/_symbols/alias.py +305 -299
  39. gamspy/_symbols/equation.py +1407 -1298
  40. gamspy/_symbols/implicits/__init__.py +11 -11
  41. gamspy/_symbols/implicits/implicit_equation.py +186 -186
  42. gamspy/_symbols/implicits/implicit_parameter.py +272 -272
  43. gamspy/_symbols/implicits/implicit_set.py +124 -124
  44. gamspy/_symbols/implicits/implicit_symbol.py +315 -315
  45. gamspy/_symbols/implicits/implicit_variable.py +255 -255
  46. gamspy/_symbols/parameter.py +648 -609
  47. gamspy/_symbols/set.py +985 -923
  48. gamspy/_symbols/symbol.py +395 -386
  49. gamspy/_symbols/universe_alias.py +182 -182
  50. gamspy/_symbols/variable.py +1101 -1017
  51. gamspy/_types.py +7 -7
  52. gamspy/_validation.py +735 -735
  53. gamspy/_workspace.py +72 -72
  54. gamspy/exceptions.py +128 -128
  55. gamspy/formulations/__init__.py +46 -46
  56. gamspy/formulations/ml/__init__.py +11 -11
  57. gamspy/formulations/ml/decision_tree_struct.py +80 -80
  58. gamspy/formulations/ml/gradient_boosting.py +203 -203
  59. gamspy/formulations/ml/random_forest.py +187 -187
  60. gamspy/formulations/ml/regression_tree.py +533 -533
  61. gamspy/formulations/nn/__init__.py +19 -19
  62. gamspy/formulations/nn/avgpool2d.py +232 -232
  63. gamspy/formulations/nn/conv1d.py +533 -533
  64. gamspy/formulations/nn/conv2d.py +529 -529
  65. gamspy/formulations/nn/linear.py +341 -341
  66. gamspy/formulations/nn/maxpool2d.py +88 -88
  67. gamspy/formulations/nn/minpool2d.py +88 -88
  68. gamspy/formulations/nn/mpool2d.py +245 -245
  69. gamspy/formulations/nn/torch_sequential.py +278 -278
  70. gamspy/formulations/piecewise.py +682 -682
  71. gamspy/formulations/result.py +119 -119
  72. gamspy/formulations/shape.py +188 -188
  73. gamspy/formulations/utils.py +173 -173
  74. gamspy/math/__init__.py +215 -215
  75. gamspy/math/activation.py +783 -767
  76. gamspy/math/log_power.py +435 -435
  77. gamspy/math/matrix.py +534 -534
  78. gamspy/math/misc.py +1709 -1625
  79. gamspy/math/probability.py +170 -170
  80. gamspy/math/trigonometric.py +232 -232
  81. gamspy/utils.py +810 -791
  82. gamspy/version.py +5 -5
  83. {gamspy-1.18.3.dist-info → gamspy-1.19.0.dist-info}/METADATA +90 -121
  84. gamspy-1.19.0.dist-info/RECORD +90 -0
  85. {gamspy-1.18.3.dist-info → gamspy-1.19.0.dist-info}/WHEEL +1 -1
  86. {gamspy-1.18.3.dist-info → gamspy-1.19.0.dist-info}/licenses/LICENSE +22 -22
  87. gamspy-1.18.3.dist-info/RECORD +0 -90
  88. {gamspy-1.18.3.dist-info → gamspy-1.19.0.dist-info}/entry_points.txt +0 -0
  89. {gamspy-1.18.3.dist-info → gamspy-1.19.0.dist-info}/top_level.txt +0 -0
@@ -1,311 +1,313 @@
1
- from __future__ import annotations
2
-
3
- import os
4
- from abc import ABC, abstractmethod
5
- from typing import TYPE_CHECKING, Literal, no_type_check
6
-
7
- import pandas as pd
8
-
9
- import gamspy._symbols as syms
10
- import gamspy.utils as utils
11
- from gamspy.exceptions import GamspyException, ValidationError
12
-
13
- if TYPE_CHECKING:
14
- import io
15
- from pathlib import Path
16
-
17
- from gamspy import Container, Model, Options
18
- from gamspy._backend.engine import EngineClient, GAMSEngine
19
- from gamspy._backend.local import Local
20
- from gamspy._backend.neos import NeosClient, NEOSServer
21
- from gamspy._symbols.symbol import Symbol
22
-
23
- SOLVE_STATUS = [
24
- "",
25
- "Normal",
26
- "Iteration",
27
- "Resource",
28
- "Solver",
29
- "EvalError",
30
- "Capability",
31
- "License",
32
- "User",
33
- "SetupErr",
34
- "SolverErr",
35
- "InternalErr",
36
- "Skipped",
37
- "SystemErr",
38
- ]
39
- HEADER = [
40
- "Solver Status",
41
- "Model Status",
42
- "Objective",
43
- "Num of Equations",
44
- "Num of Variables",
45
- "Model Type",
46
- "Solver",
47
- "Solver Time",
48
- ]
49
-
50
-
51
- @no_type_check
52
- def backend_factory(
53
- container: Container,
54
- options: Options | None = None,
55
- solver: str | None = None,
56
- solver_options: dict | Path | None = None,
57
- output: io.TextIOWrapper | None = None,
58
- backend: Literal["local", "engine", "neos"] = "local",
59
- client: EngineClient | NeosClient | None = None,
60
- model: Model | None = None,
61
- load_symbols: list[Symbol] | None = None,
62
- ) -> Local | GAMSEngine | NEOSServer:
63
- if backend == "neos":
64
- from gamspy._backend.neos import NEOSServer
65
-
66
- return NEOSServer(
67
- container,
68
- options,
69
- solver,
70
- solver_options,
71
- client,
72
- output,
73
- model,
74
- load_symbols,
75
- )
76
- elif backend == "engine":
77
- from gamspy._backend.engine import GAMSEngine
78
-
79
- return GAMSEngine(
80
- container,
81
- client,
82
- options,
83
- solver,
84
- solver_options,
85
- output,
86
- model,
87
- load_symbols,
88
- )
89
- elif backend == "local":
90
- from gamspy._backend.local import Local
91
-
92
- return Local(
93
- container,
94
- options,
95
- solver,
96
- solver_options,
97
- output,
98
- model,
99
- load_symbols,
100
- )
101
-
102
- raise ValidationError(
103
- f"`{backend}` is not a valid backend. Possible backends:"
104
- " local, engine, and neos"
105
- )
106
-
107
-
108
- def _cast_values(
109
- objective_value: str,
110
- num_equations: str,
111
- num_variables: str,
112
- solver_time: str,
113
- ) -> tuple[float, int | float, int | float, float]:
114
- objective = float("nan") if objective_value == "NA" else float(objective_value)
115
- equations = float("nan") if num_equations == "NA" else int(num_equations)
116
- variables = float("nan") if num_variables == "NA" else int(num_variables)
117
- time = float("nan") if solver_time == "NA" else float(solver_time)
118
-
119
- return objective, equations, variables, time
120
-
121
-
122
- class Backend(ABC):
123
- def __init__(
124
- self,
125
- backend_type: str,
126
- container: Container,
127
- model: Model | None,
128
- options: Options,
129
- solver: str | None,
130
- solver_options: dict | Path | None,
131
- output: io.TextIOWrapper | None,
132
- load_symbols: list[Symbol] | None,
133
- ):
134
- self.backend_type = backend_type
135
- self.container = container
136
- self.model = model
137
- self.options = options
138
- self.solver = solver
139
- self.solver_options = solver_options
140
- self.output = output
141
- if load_symbols is not None:
142
- self.load_symbols: list[str] = [
143
- symbol.name # type: ignore
144
- for symbol in load_symbols
145
- ]
146
-
147
- self.job_name = self.get_job_name()
148
- self.gms_file = self.job_name + ".gms"
149
- self.lst_file = self.job_name + ".lst"
150
- self.pf_file = self.job_name + ".pf"
151
- self.restart_file = self.job_name + ".g00"
152
- self.trace_file = self.job_name + ".txt"
153
-
154
- @abstractmethod
155
- def is_async(self): ...
156
-
157
- @abstractmethod
158
- def run(
159
- self,
160
- relaxed_domain_mapping: bool = False,
161
- gams_to_gamspy: bool = False,
162
- ): ...
163
-
164
- def get_job_name(self):
165
- job_name = self.container._job
166
-
167
- if self.container._debugging_level == "keep":
168
- job_name = os.path.join(
169
- self.container.working_directory,
170
- "_" + utils._get_unique_name(),
171
- )
172
- self.container._job = job_name
173
- self.container._gdx_in = f"{job_name}in.gdx"
174
- self.container._gdx_out = f"{job_name}out.gdx"
175
-
176
- return job_name
177
-
178
- def preprocess(self):
179
- modified_names = self.container._get_modified_symbols()
180
-
181
- if len(modified_names) != 0:
182
- try:
183
- self.container.write(
184
- self.container._gdx_in, modified_names, eps_to_zero=False
185
- )
186
- except Exception as e:
187
- # Unfortunately, GTP raises a blind exception here. Turn it into a GamspyException.
188
- raise GamspyException(str(e)) from e
189
-
190
- gdx_in = self.container._gdx_in
191
- if self.backend_type == "engine":
192
- gdx_in = os.path.basename(gdx_in)
193
- elif self.backend_type == "neos":
194
- gdx_in = "in.gdx"
195
- gams_string = self.container._generate_gams_string(gdx_in, modified_names)
196
- self.make_unmodified(modified_names)
197
-
198
- return gams_string
199
-
200
- def load_records(self, relaxed_domain_mapping: bool = False):
201
- if hasattr(self, "load_symbols"):
202
- symbols = self.load_symbols
203
- else:
204
- symbols = utils._get_symbol_names_from_gdx(
205
- self.container.system_directory, self.container._gdx_out
206
- )
207
- filtered_names = []
208
- for name in symbols:
209
- # addGamsCode symbols
210
- if name not in self.container:
211
- filtered_names.append(name)
212
- continue
213
-
214
- symbol = self.container[name]
215
- if type(symbol) is syms.Alias:
216
- filtered_names.append(name)
217
- continue
218
-
219
- if symbol.synchronize:
220
- filtered_names.append(name)
221
-
222
- symbols = filtered_names
223
-
224
- if len(symbols) != 0:
225
- self.container._load_records_from_gdx(self.container._gdx_out, symbols)
226
- self.make_unmodified(symbols)
227
-
228
- if relaxed_domain_mapping:
229
- # Best attempt approach to map relaxed domain to actual symbols
230
- for name in symbols:
231
- symbol = self.container[name]
232
-
233
- new_domain = []
234
- for elem in symbol.domain:
235
- if type(elem) is str and elem != "*" and elem in self.container:
236
- new_domain.append(self.container[elem])
237
- else:
238
- new_domain.append(elem)
239
-
240
- symbol.domain = new_domain
241
- symbol.dimension = len(new_domain)
242
- if type(symbol) in (syms.Variable, syms.Equation):
243
- symbol._update_attr_domains()
244
-
245
- def parse_listings(self):
246
- listing_file = (
247
- self.options.listing_file
248
- if self.options.listing_file
249
- else self.job_name + ".lst"
250
- )
251
- if self.options.equation_listing_limit:
252
- utils._parse_generated_equations(self.model, listing_file)
253
-
254
- if self.options.variable_listing_limit:
255
- utils._parse_generated_variables(self.model, listing_file)
256
-
257
- def prepare_summary(self, trace_file: str) -> pd.DataFrame:
258
- from gamspy._model import ModelStatus
259
-
260
- with open(trace_file, encoding="utf-8") as file:
261
- line = file.readlines()[-1]
262
- (
263
- _,
264
- model_type,
265
- solver_name,
266
- _,
267
- _,
268
- _,
269
- _,
270
- num_equations,
271
- num_variables,
272
- _,
273
- _,
274
- _,
275
- _,
276
- model_status,
277
- solver_status,
278
- objective_value,
279
- _,
280
- solver_time,
281
- _,
282
- _,
283
- _,
284
- _,
285
- ) = line.split(",")
286
-
287
- objective_value, num_equations, num_variables, solver_time = _cast_values(
288
- objective_value, num_equations, num_variables, solver_time
289
- )
290
-
291
- dataframe = pd.DataFrame(
292
- [
293
- [
294
- SOLVE_STATUS[int(solver_status)],
295
- ModelStatus(int(model_status)).name,
296
- objective_value,
297
- num_equations,
298
- num_variables,
299
- model_type,
300
- solver_name,
301
- solver_time,
302
- ]
303
- ],
304
- columns=HEADER,
305
- )
306
- return dataframe
307
-
308
- def make_unmodified(self, modified_names: list[str]):
309
- for name in modified_names:
310
- if not name.startswith("autogenerated_"):
311
- self.container[name].modified = False
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ from abc import ABC, abstractmethod
5
+ from typing import TYPE_CHECKING, Literal, no_type_check
6
+
7
+ import pandas as pd
8
+
9
+ import gamspy._symbols as syms
10
+ import gamspy.utils as utils
11
+ from gamspy.exceptions import GamspyException, ValidationError
12
+
13
+ if TYPE_CHECKING:
14
+ import io
15
+ from pathlib import Path
16
+
17
+ from gamspy import Container, Model, Options
18
+ from gamspy._backend.engine import EngineClient, GAMSEngine
19
+ from gamspy._backend.local import Local
20
+ from gamspy._backend.neos import NeosClient, NEOSServer
21
+ from gamspy._symbols.symbol import Symbol
22
+
23
+ SOLVE_STATUS = [
24
+ "",
25
+ "Normal",
26
+ "Iteration",
27
+ "Resource",
28
+ "Solver",
29
+ "EvalError",
30
+ "Capability",
31
+ "License",
32
+ "User",
33
+ "SetupErr",
34
+ "SolverErr",
35
+ "InternalErr",
36
+ "Skipped",
37
+ "SystemErr",
38
+ ]
39
+ HEADER = [
40
+ "Solver Status",
41
+ "Model Status",
42
+ "Objective",
43
+ "Num of Equations",
44
+ "Num of Variables",
45
+ "Model Type",
46
+ "Solver",
47
+ "Solver Time",
48
+ ]
49
+
50
+
51
+ @no_type_check
52
+ def backend_factory(
53
+ container: Container,
54
+ options: Options | None = None,
55
+ solver: str | None = None,
56
+ solver_options: dict | Path | None = None,
57
+ output: io.TextIOWrapper | None = None,
58
+ backend: Literal["local", "engine", "neos"] = "local",
59
+ client: EngineClient | NeosClient | None = None,
60
+ model: Model | None = None,
61
+ load_symbols: list[Symbol] | None = None,
62
+ ) -> Local | GAMSEngine | NEOSServer:
63
+ if backend == "neos":
64
+ from gamspy._backend.neos import NEOSServer
65
+
66
+ return NEOSServer(
67
+ container,
68
+ options,
69
+ solver,
70
+ solver_options,
71
+ client,
72
+ output,
73
+ model,
74
+ load_symbols,
75
+ )
76
+ elif backend == "engine":
77
+ from gamspy._backend.engine import GAMSEngine
78
+
79
+ return GAMSEngine(
80
+ container,
81
+ client,
82
+ options,
83
+ solver,
84
+ solver_options,
85
+ output,
86
+ model,
87
+ load_symbols,
88
+ )
89
+ elif backend == "local":
90
+ from gamspy._backend.local import Local
91
+
92
+ return Local(
93
+ container,
94
+ options,
95
+ solver,
96
+ solver_options,
97
+ output,
98
+ model,
99
+ load_symbols,
100
+ )
101
+
102
+ raise ValidationError(
103
+ f"`{backend}` is not a valid backend. Possible backends:"
104
+ " local, engine, and neos"
105
+ )
106
+
107
+
108
+ def _cast_values(
109
+ objective_value: str,
110
+ num_equations: str,
111
+ num_variables: str,
112
+ solver_time: str,
113
+ ) -> tuple[float, int | float, int | float, float]:
114
+ objective = float("nan") if objective_value == "NA" else float(objective_value)
115
+ equations = float("nan") if num_equations == "NA" else int(num_equations)
116
+ variables = float("nan") if num_variables == "NA" else int(num_variables)
117
+ time = float("nan") if solver_time == "NA" else float(solver_time)
118
+
119
+ return objective, equations, variables, time
120
+
121
+
122
+ class Backend(ABC):
123
+ def __init__(
124
+ self,
125
+ backend_type: str,
126
+ container: Container,
127
+ model: Model | None,
128
+ options: Options,
129
+ solver: str | None,
130
+ solver_options: dict | Path | None,
131
+ output: io.TextIOWrapper | None,
132
+ load_symbols: list[Symbol] | None,
133
+ ):
134
+ self.backend_type = backend_type
135
+ self.container = container
136
+ self.model = model
137
+ self.options = options
138
+ self.solver = solver
139
+ self.solver_options = solver_options
140
+ self.output = output
141
+ if load_symbols is not None:
142
+ self.load_symbols: list[str] = [
143
+ symbol.name # type: ignore
144
+ for symbol in load_symbols
145
+ ]
146
+
147
+ self.job_name = self.get_job_name()
148
+ self.gms_file = self.job_name + ".gms"
149
+ self.lst_file = self.job_name + ".lst"
150
+ self.pf_file = self.job_name + ".pf"
151
+ self.restart_file = self.job_name + ".g00"
152
+ self.trace_file = self.job_name + ".txt"
153
+
154
+ @abstractmethod
155
+ def is_async(self): ...
156
+
157
+ @abstractmethod
158
+ def run(
159
+ self,
160
+ relaxed_domain_mapping: bool = False,
161
+ gams_to_gamspy: bool = False,
162
+ ): ...
163
+
164
+ def get_job_name(self):
165
+ job_name = self.container._job
166
+
167
+ if self.container._debugging_level == "keep":
168
+ job_name = os.path.join(
169
+ self.container.working_directory,
170
+ "_" + utils._get_unique_name(),
171
+ )
172
+ self.container._job = job_name
173
+ self.container._gdx_in = f"{job_name}in.gdx"
174
+ self.container._gdx_out = f"{job_name}out.gdx"
175
+
176
+ return job_name
177
+
178
+ def preprocess(self):
179
+ modified_names = self.container._get_modified_symbols()
180
+
181
+ if len(modified_names) != 0:
182
+ try:
183
+ self.container.write(
184
+ self.container._gdx_in, modified_names, eps_to_zero=False
185
+ )
186
+ except Exception as e:
187
+ # Unfortunately, GTP raises a blind exception here. Turn it into a GamspyException.
188
+ raise GamspyException(str(e)) from e
189
+
190
+ gdx_in = self.container._gdx_in
191
+ if self.backend_type == "engine":
192
+ gdx_in = os.path.basename(gdx_in)
193
+ elif self.backend_type == "neos":
194
+ gdx_in = "in.gdx"
195
+ gams_string = self.container._generate_gams_string(gdx_in, modified_names)
196
+ self.make_unmodified(modified_names)
197
+
198
+ return gams_string
199
+
200
+ def load_records(self, relaxed_domain_mapping: bool = False):
201
+ if hasattr(self, "load_symbols"):
202
+ symbols = self.load_symbols
203
+ else:
204
+ symbols = utils._get_symbol_names_from_gdx(
205
+ self.container.system_directory, self.container._gdx_out
206
+ )
207
+ filtered_names = []
208
+ for name in symbols:
209
+ # addGamsCode symbols
210
+ if name not in self.container:
211
+ filtered_names.append(name)
212
+ continue
213
+
214
+ symbol = self.container[name]
215
+ if type(symbol) is syms.Alias:
216
+ filtered_names.append(name)
217
+ continue
218
+
219
+ if symbol.synchronize:
220
+ filtered_names.append(name)
221
+
222
+ symbols = filtered_names
223
+
224
+ if len(symbols) != 0:
225
+ self.container._load_records_from_gdx(
226
+ self.container._gdx_out, symbols, create_if_not_declared=True
227
+ )
228
+ self.make_unmodified(symbols)
229
+
230
+ if relaxed_domain_mapping:
231
+ # Best attempt approach to map relaxed domain to actual symbols
232
+ for name in symbols:
233
+ symbol = self.container[name]
234
+
235
+ new_domain = []
236
+ for elem in symbol.domain:
237
+ if type(elem) is str and elem != "*" and elem in self.container:
238
+ new_domain.append(self.container[elem])
239
+ else:
240
+ new_domain.append(elem)
241
+
242
+ symbol.domain = new_domain
243
+ symbol.dimension = len(new_domain)
244
+ if type(symbol) in (syms.Variable, syms.Equation):
245
+ symbol._update_attr_domains()
246
+
247
+ def parse_listings(self):
248
+ listing_file = (
249
+ self.options.listing_file
250
+ if self.options.listing_file
251
+ else self.job_name + ".lst"
252
+ )
253
+ if self.options.equation_listing_limit:
254
+ utils._parse_generated_equations(self.model, listing_file)
255
+
256
+ if self.options.variable_listing_limit:
257
+ utils._parse_generated_variables(self.model, listing_file)
258
+
259
+ def prepare_summary(self, trace_file: str) -> pd.DataFrame:
260
+ from gamspy._model import ModelStatus
261
+
262
+ with open(trace_file, encoding="utf-8") as file:
263
+ line = file.readlines()[-1]
264
+ (
265
+ _,
266
+ model_type,
267
+ solver_name,
268
+ _,
269
+ _,
270
+ _,
271
+ _,
272
+ num_equations,
273
+ num_variables,
274
+ _,
275
+ _,
276
+ _,
277
+ _,
278
+ model_status,
279
+ solver_status,
280
+ objective_value,
281
+ _,
282
+ solver_time,
283
+ _,
284
+ _,
285
+ _,
286
+ _,
287
+ ) = line.split(",")
288
+
289
+ objective_value, num_equations, num_variables, solver_time = _cast_values(
290
+ objective_value, num_equations, num_variables, solver_time
291
+ )
292
+
293
+ dataframe = pd.DataFrame(
294
+ [
295
+ [
296
+ SOLVE_STATUS[int(solver_status)],
297
+ ModelStatus(int(model_status)).name,
298
+ objective_value,
299
+ num_equations,
300
+ num_variables,
301
+ model_type,
302
+ solver_name,
303
+ solver_time,
304
+ ]
305
+ ],
306
+ columns=HEADER,
307
+ )
308
+ return dataframe
309
+
310
+ def make_unmodified(self, modified_names: list[str]):
311
+ for name in modified_names:
312
+ if not name.startswith("autogenerated_"):
313
+ self.container[name].modified = False