gamspy 1.17.0__py3-none-any.whl → 1.17.2__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.
- gamspy/__init__.py +1 -1
- gamspy/_algebra/expression.py +12 -14
- gamspy/_algebra/operation.py +3 -2
- gamspy/_cli/install.py +4 -2
- gamspy/_cli/show.py +4 -1
- gamspy/_cli/uninstall.py +4 -2
- gamspy/_config.py +1 -1
- gamspy/_container.py +27 -7
- gamspy/_convert.py +18 -17
- gamspy/_database.py +1 -1
- gamspy/_miro.py +1 -1
- gamspy/_model.py +146 -13
- gamspy/_model_instance.py +3 -2
- gamspy/_options.py +6 -6
- gamspy/_serialization.py +8 -5
- gamspy/_symbols/alias.py +4 -3
- gamspy/_symbols/equation.py +10 -3
- gamspy/_symbols/implicits/implicit_parameter.py +4 -3
- gamspy/_symbols/implicits/implicit_set.py +3 -2
- gamspy/_symbols/implicits/implicit_symbol.py +7 -7
- gamspy/_symbols/implicits/implicit_variable.py +4 -3
- gamspy/_symbols/parameter.py +6 -5
- gamspy/_symbols/set.py +4 -3
- gamspy/_symbols/universe_alias.py +1 -1
- gamspy/_symbols/variable.py +23 -5
- gamspy/_types.py +0 -1
- gamspy/_validation.py +5 -10
- gamspy/exceptions.py +1 -1
- gamspy/formulations/__init__.py +2 -0
- gamspy/formulations/nn/maxpool2d.py +5 -1
- gamspy/formulations/nn/minpool2d.py +5 -1
- gamspy/formulations/nn/torch_sequential.py +80 -8
- gamspy/formulations/result.py +119 -0
- gamspy/math/__init__.py +4 -0
- gamspy/math/activation.py +195 -8
- gamspy/math/matrix.py +2 -2
- gamspy/math/misc.py +1 -1
- gamspy/utils.py +61 -47
- {gamspy-1.17.0.dist-info → gamspy-1.17.2.dist-info}/METADATA +3 -2
- {gamspy-1.17.0.dist-info → gamspy-1.17.2.dist-info}/RECORD +44 -43
- {gamspy-1.17.0.dist-info → gamspy-1.17.2.dist-info}/WHEEL +0 -0
- {gamspy-1.17.0.dist-info → gamspy-1.17.2.dist-info}/entry_points.txt +0 -0
- {gamspy-1.17.0.dist-info → gamspy-1.17.2.dist-info}/licenses/LICENSE +0 -0
- {gamspy-1.17.0.dist-info → gamspy-1.17.2.dist-info}/top_level.txt +0 -0
gamspy/_model.py
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import inspect
|
|
4
|
-
import io
|
|
5
4
|
import logging
|
|
6
5
|
import os
|
|
7
6
|
import threading
|
|
8
7
|
import warnings
|
|
9
8
|
from collections.abc import Sequence
|
|
10
9
|
from enum import Enum
|
|
10
|
+
from pathlib import Path
|
|
11
11
|
from typing import TYPE_CHECKING, Any, ClassVar
|
|
12
12
|
|
|
13
13
|
from gams.core.gdx import GMS_UEL_IDENT_SIZE
|
|
@@ -37,6 +37,7 @@ from gamspy._options import (
|
|
|
37
37
|
from gamspy.exceptions import GamspyException, ValidationError
|
|
38
38
|
|
|
39
39
|
if TYPE_CHECKING:
|
|
40
|
+
import io
|
|
40
41
|
from typing import Literal
|
|
41
42
|
|
|
42
43
|
import pandas as pd
|
|
@@ -72,21 +73,52 @@ class Problem(Enum):
|
|
|
72
73
|
"""An enumeration for problem all problem types"""
|
|
73
74
|
|
|
74
75
|
LP = "LP"
|
|
76
|
+
"Linear Programming"
|
|
77
|
+
|
|
75
78
|
NLP = "NLP"
|
|
79
|
+
"""Non-Linear Programming"""
|
|
80
|
+
|
|
76
81
|
QCP = "QCP"
|
|
82
|
+
"""Quadratically Constrained Programs"""
|
|
83
|
+
|
|
77
84
|
DNLP = "DNLP"
|
|
85
|
+
"""Nonlinear Programming with Discontinuous Derivatives"""
|
|
86
|
+
|
|
78
87
|
MIP = "MIP"
|
|
88
|
+
"""Mixed Integer Programming"""
|
|
89
|
+
|
|
79
90
|
RMIP = "RMIP"
|
|
91
|
+
"""Relaxed Mixed Integer Program"""
|
|
92
|
+
|
|
80
93
|
MINLP = "MINLP"
|
|
94
|
+
"""Mixed Integer Nonlinear Program"""
|
|
95
|
+
|
|
81
96
|
RMINLP = "RMINLP"
|
|
97
|
+
"""Relaxed Mixed Integer Nonlinear Program"""
|
|
98
|
+
|
|
82
99
|
MIQCP = "MIQCP"
|
|
100
|
+
"""Mixed Integer Quadratically Constrained Program"""
|
|
101
|
+
|
|
83
102
|
RMIQCP = "RMIQCP"
|
|
103
|
+
"""Relaxed Mixed Integer Quadratically Constrained Program"""
|
|
104
|
+
|
|
84
105
|
MCP = "MCP"
|
|
106
|
+
"""Mixed Complementarity Problem"""
|
|
107
|
+
|
|
85
108
|
CNS = "CNS"
|
|
109
|
+
"""Constrained Nonlinear System"""
|
|
110
|
+
|
|
86
111
|
MPEC = "MPEC"
|
|
112
|
+
"""Mathematical Programs with Equilibrium Constraints"""
|
|
113
|
+
|
|
87
114
|
RMPEC = "RMPEC"
|
|
115
|
+
"""Relaxed Mathematical Program with Equilibrium Constraints"""
|
|
116
|
+
|
|
88
117
|
EMP = "EMP"
|
|
118
|
+
"""Extended Mathematical Program"""
|
|
119
|
+
|
|
89
120
|
MPSGE = "MPSGE"
|
|
121
|
+
"""General Equilibrium"""
|
|
90
122
|
|
|
91
123
|
@classmethod
|
|
92
124
|
def values(cls):
|
|
@@ -101,8 +133,13 @@ class Sense(Enum):
|
|
|
101
133
|
"""An enumeration for sense types"""
|
|
102
134
|
|
|
103
135
|
MIN = "MIN"
|
|
136
|
+
"""Minimize the objective."""
|
|
137
|
+
|
|
104
138
|
MAX = "MAX"
|
|
139
|
+
"""Maximize the objective."""
|
|
140
|
+
|
|
105
141
|
FEASIBILITY = "FEASIBILITY"
|
|
142
|
+
"""Assess feasibility."""
|
|
106
143
|
|
|
107
144
|
@classmethod
|
|
108
145
|
def values(cls):
|
|
@@ -117,63 +154,156 @@ class ModelStatus(Enum):
|
|
|
117
154
|
"""An enumeration for model status types"""
|
|
118
155
|
|
|
119
156
|
OptimalGlobal = 1
|
|
157
|
+
"""The solution is optimal, that is, it is feasible (within tolerances) and it has been proven that no other feasible solution with better objective value exists."""
|
|
158
|
+
|
|
120
159
|
OptimalLocal = 2
|
|
160
|
+
"""A local optimum for an NLP has been found. That is, a solution that is feasible (within tolerances) and it has been proven that there exists a neighborhood of this solution in which no other feasible solution with better objective value exists."""
|
|
161
|
+
|
|
121
162
|
Unbounded = 3
|
|
163
|
+
"""The solution is unbounded. This message is reliable if the problem is linear, but occasionally it appears for difficult nonlinear problems that are not truly unbounded, but that lack some strategically placed bounds to limit the variables to sensible values."""
|
|
164
|
+
|
|
122
165
|
InfeasibleGlobal = 4
|
|
166
|
+
"""The problem has been proven to be infeasible. If this was not intended, something is probably misspecified in the logic or the data."""
|
|
167
|
+
|
|
123
168
|
InfeasibleLocal = 5
|
|
169
|
+
"""No feasible point could be found for the NLP problem from the given starting point. It does not necessarily mean that no feasible point exists."""
|
|
170
|
+
|
|
124
171
|
InfeasibleIntermed = 6
|
|
172
|
+
"""The current solution is not feasible, but the solver stopped, either because of a limit (for example, iteration or resource) or because of some sort of difficulty. The solver status will give more information."""
|
|
173
|
+
|
|
125
174
|
Feasible = 7
|
|
175
|
+
"""A feasible solution to a problem without discrete variables has been found."""
|
|
176
|
+
|
|
126
177
|
Integer = 8
|
|
178
|
+
"""A feasible solution to a problem with discrete variables has been found."""
|
|
179
|
+
|
|
127
180
|
NonIntegerIntermed = 9
|
|
181
|
+
"""An incomplete solution to a problem with discrete variables. A feasible solution has not yet been found."""
|
|
182
|
+
|
|
128
183
|
IntegerInfeasible = 10
|
|
184
|
+
"""It has been proven that there is no feasible solution to a problem with discrete variables."""
|
|
185
|
+
|
|
129
186
|
LicenseError = 11
|
|
187
|
+
"""The solver cannot find the appropriate license key needed to use a specific subsolver."""
|
|
188
|
+
|
|
130
189
|
ErrorUnknown = 12
|
|
190
|
+
"""After a solver error the model status is unknown."""
|
|
191
|
+
|
|
131
192
|
ErrorNoSolution = 13
|
|
193
|
+
"""An error occurred and no solution has been returned. No solution will be returned to GAMS because of errors in the solution process."""
|
|
194
|
+
|
|
132
195
|
NoSolutionReturned = 14
|
|
196
|
+
"""A solution is not expected for this solve. For example, the CONVERT solver only reformats the model but does not give a solution."""
|
|
197
|
+
|
|
133
198
|
SolvedUnique = 15
|
|
199
|
+
"""Indicates the solution returned is unique, i.e. no other solution exists. Used for CNS models. Examples where this status could be returned include non-singular linear models, triangular models with constant non-zero elements on the diagonal, and triangular models where the functions are monotone in the variable on the diagonal."""
|
|
200
|
+
|
|
134
201
|
Solved = 16
|
|
202
|
+
"""Indicates the model has been solved: used for CNS models. The solution might or might not be unique. If the solver uses status SOLVED SINGULAR wherever possible then this status implies that the Jacobian is non-singular, i.e. that the solution is at least locally unique."""
|
|
203
|
+
|
|
135
204
|
SolvedSingular = 17
|
|
205
|
+
"""Indicates the CNS model has been solved, but the Jacobian is singular at the solution. This can indicate that other solutions exist, either along a line (for linear models) or a curve (for nonlinear models) including the solution returned."""
|
|
206
|
+
|
|
136
207
|
UnboundedNoSolution = 18
|
|
208
|
+
"""The model is unbounded and no solution can be provided."""
|
|
209
|
+
|
|
137
210
|
InfeasibleNoSolution = 19
|
|
211
|
+
"""The model is infeasible and no solution can be provided."""
|
|
138
212
|
|
|
139
213
|
|
|
140
214
|
class SolveStatus(Enum):
|
|
141
215
|
"""An enumeration for solve status types"""
|
|
142
216
|
|
|
143
217
|
NormalCompletion = 1
|
|
218
|
+
"""The solver terminated in a normal way."""
|
|
219
|
+
|
|
144
220
|
IterationInterrupt = 2
|
|
221
|
+
"""The solver was interrupted because it used too many iterations. The option `iteration_limit` may be used to increase the iteration limit if everything seems normal."""
|
|
222
|
+
|
|
145
223
|
ResourceInterrupt = 3
|
|
224
|
+
"""The solver was interrupted because it used too much time. The option `time_limit` may be used to increase the time limit if everything seems normal."""
|
|
225
|
+
|
|
146
226
|
TerminatedBySolver = 4
|
|
227
|
+
"""The solver encountered some difficulty and was unable to continue."""
|
|
228
|
+
|
|
147
229
|
EvaluationInterrupt = 5
|
|
230
|
+
"""Too many evaluations of nonlinear terms at undefined values. We recommend to use variable bounds to prevent forbidden operations, such as division by zero. The rows in which the errors occur are listed just before the solution."""
|
|
231
|
+
|
|
148
232
|
CapabilityError = 6
|
|
233
|
+
"""The solver does not have the capability required by the model. For example, some solvers do not support certain types of discrete variables or support a more limited set of functions than other solvers."""
|
|
234
|
+
|
|
149
235
|
LicenseError = 7
|
|
236
|
+
"""The solver cannot find the appropriate license key needed to use a specific subsolver."""
|
|
237
|
+
|
|
150
238
|
UserInterrupt = 8
|
|
239
|
+
"""The user has sent a signal to interrupt the solver."""
|
|
240
|
+
|
|
151
241
|
SetupError = 9
|
|
242
|
+
"""The solver encountered a fatal failure during problem set-up time."""
|
|
243
|
+
|
|
152
244
|
SolverError = 10
|
|
245
|
+
"""The solver encountered a fatal error."""
|
|
246
|
+
|
|
153
247
|
InternalError = 11
|
|
248
|
+
"""The solver encountered an internal fatal error."""
|
|
249
|
+
|
|
154
250
|
Skipped = 12
|
|
251
|
+
"""The entire solve step has been skipped."""
|
|
252
|
+
|
|
155
253
|
SystemError = 13
|
|
254
|
+
"""This indicates a completely unknown or unexpected error condition."""
|
|
156
255
|
|
|
157
256
|
|
|
158
257
|
class FileFormat(Enum):
|
|
159
258
|
"""An enumeration for file format types"""
|
|
160
259
|
|
|
161
260
|
AMPL = "ampl.mod"
|
|
261
|
+
"""AMPL input format."""
|
|
262
|
+
|
|
162
263
|
AMPLNL = "ampl.nl"
|
|
264
|
+
"""AMPL nl format."""
|
|
265
|
+
|
|
163
266
|
CPLEXLP = "cplex.lp"
|
|
267
|
+
"""CPLEX LP format."""
|
|
268
|
+
|
|
164
269
|
CPLEXMPS = "cplex.mps"
|
|
270
|
+
"""CPLEX MPS format."""
|
|
271
|
+
|
|
165
272
|
GAMSDict = "dict.txt"
|
|
273
|
+
"""GAMS dictionary format."""
|
|
274
|
+
|
|
166
275
|
GAMSDictMap = "dictmap.gdx"
|
|
276
|
+
"""GAMS dictionary map format."""
|
|
277
|
+
|
|
167
278
|
GAMSJacobian = "jacobian.gms"
|
|
279
|
+
"""Jacobian in GAMS."""
|
|
280
|
+
|
|
168
281
|
GAMSPyJacobian = "jacobian.py"
|
|
282
|
+
"""Jacobian in GAMSPy."""
|
|
283
|
+
|
|
169
284
|
GDXJacobian = "jacobian.gdx"
|
|
285
|
+
"""GDX file with model data incl. Jacobian and Hessian evaluated at current point."""
|
|
286
|
+
|
|
170
287
|
FileList = "files.txt"
|
|
288
|
+
"""List of file formats generated."""
|
|
289
|
+
|
|
171
290
|
FixedMPS = "fixed.mps"
|
|
291
|
+
"""Fixed format mps file."""
|
|
292
|
+
|
|
172
293
|
GAMS = "gams.gms"
|
|
294
|
+
"""GAMS scalar model."""
|
|
295
|
+
|
|
173
296
|
JuMP = "jump.jl"
|
|
297
|
+
"""JuMP scalar model."""
|
|
298
|
+
|
|
174
299
|
LINGO = "lingo.lng"
|
|
300
|
+
"""Lingo format."""
|
|
301
|
+
|
|
175
302
|
OSiL = "osil.xml"
|
|
303
|
+
"""Optimization Services instance Language (OSiL) format."""
|
|
304
|
+
|
|
176
305
|
Pyomo = "pyomo.py"
|
|
306
|
+
"""Pyomo concrete scalar model."""
|
|
177
307
|
|
|
178
308
|
|
|
179
309
|
INTERRUPT_STATUS = [
|
|
@@ -394,7 +524,7 @@ class Model:
|
|
|
394
524
|
|
|
395
525
|
# matches
|
|
396
526
|
if self._matches is not None:
|
|
397
|
-
matches: dict =
|
|
527
|
+
matches: dict = {}
|
|
398
528
|
for key, value in self._matches.items():
|
|
399
529
|
if isinstance(key, gp.Equation):
|
|
400
530
|
if isinstance(value, gp.Variable):
|
|
@@ -924,19 +1054,19 @@ class Model:
|
|
|
924
1054
|
return assignment
|
|
925
1055
|
|
|
926
1056
|
def _generate_solve_string(self) -> str:
|
|
927
|
-
|
|
1057
|
+
solve_statement = [f"solve {self.name} using {self.problem}"]
|
|
928
1058
|
|
|
929
1059
|
if self.sense == gp.Sense.FEASIBILITY:
|
|
930
1060
|
# Set sense as min or max for feasibility
|
|
931
1061
|
self.sense = gp.Sense("MIN")
|
|
932
1062
|
|
|
933
1063
|
if self.problem not in (Problem.MCP, Problem.CNS, Problem.EMP):
|
|
934
|
-
|
|
1064
|
+
solve_statement.append(str(self.sense))
|
|
935
1065
|
|
|
936
1066
|
if self._objective_variable is not None:
|
|
937
|
-
|
|
1067
|
+
solve_statement.append(self._objective_variable.gamsRepr())
|
|
938
1068
|
|
|
939
|
-
return
|
|
1069
|
+
return " ".join(solve_statement)
|
|
940
1070
|
|
|
941
1071
|
def _add_runtime_options(self, options: Options, backend: str = "local") -> None:
|
|
942
1072
|
for key, value in options.model_dump(exclude_none=True).items():
|
|
@@ -1058,7 +1188,7 @@ class Model:
|
|
|
1058
1188
|
|
|
1059
1189
|
def convert(
|
|
1060
1190
|
self,
|
|
1061
|
-
path: str,
|
|
1191
|
+
path: str | Path,
|
|
1062
1192
|
file_format: FileFormat | Sequence[FileFormat],
|
|
1063
1193
|
options: ConvertOptions | None = None,
|
|
1064
1194
|
) -> None:
|
|
@@ -1067,7 +1197,7 @@ class Model:
|
|
|
1067
1197
|
|
|
1068
1198
|
Parameters
|
|
1069
1199
|
----------
|
|
1070
|
-
path : str
|
|
1200
|
+
path : str | Path
|
|
1071
1201
|
Path to the directory where the converted model files will be saved.
|
|
1072
1202
|
file_format : FileFormat | Sequence[FileFormat]
|
|
1073
1203
|
File format(s) to convert the model to. Can be a single FileFormat or a list of FileFormats.
|
|
@@ -1090,6 +1220,7 @@ class Model:
|
|
|
1090
1220
|
>>> my_model.convert("output_directory", [gp.FileFormat.GAMS, gp.FileFormat.AMPL])
|
|
1091
1221
|
|
|
1092
1222
|
"""
|
|
1223
|
+
path = Path(path)
|
|
1093
1224
|
os.makedirs(path, exist_ok=True)
|
|
1094
1225
|
solver_options = get_convert_solver_options(path, file_format, options)
|
|
1095
1226
|
self.solve(solver="convert", solver_options=solver_options)
|
|
@@ -1358,7 +1489,7 @@ class Model:
|
|
|
1358
1489
|
if isinstance(key, gp.Equation):
|
|
1359
1490
|
equations_in_matches.append(key)
|
|
1360
1491
|
else:
|
|
1361
|
-
equations_in_matches +=
|
|
1492
|
+
equations_in_matches += list(key)
|
|
1362
1493
|
|
|
1363
1494
|
equations = []
|
|
1364
1495
|
for equation in self.equations:
|
|
@@ -1413,7 +1544,7 @@ class Model:
|
|
|
1413
1544
|
|
|
1414
1545
|
def toGams(
|
|
1415
1546
|
self,
|
|
1416
|
-
path: str,
|
|
1547
|
+
path: str | Path,
|
|
1417
1548
|
options: Options | None = None,
|
|
1418
1549
|
*,
|
|
1419
1550
|
dump_gams_state: bool = False,
|
|
@@ -1423,7 +1554,7 @@ class Model:
|
|
|
1423
1554
|
|
|
1424
1555
|
Parameters
|
|
1425
1556
|
----------
|
|
1426
|
-
path : str
|
|
1557
|
+
path : str | Path
|
|
1427
1558
|
Path to the directory which will contain the GAMS model.
|
|
1428
1559
|
options : Options | None, optional
|
|
1429
1560
|
GAMSPy options, by default None
|
|
@@ -1440,18 +1571,20 @@ class Model:
|
|
|
1440
1571
|
f"`options` must be of type gp.Options of found {type(options)}"
|
|
1441
1572
|
)
|
|
1442
1573
|
|
|
1574
|
+
path = Path(path)
|
|
1443
1575
|
converter = GamsConverter(self, path, options, dump_gams_state)
|
|
1444
1576
|
converter.convert()
|
|
1445
1577
|
|
|
1446
|
-
def toLatex(self, path: str, generate_pdf: bool = False) -> None:
|
|
1578
|
+
def toLatex(self, path: str | Path, generate_pdf: bool = False) -> None:
|
|
1447
1579
|
"""
|
|
1448
1580
|
Generates a latex file that contains the model definition under path/<model_name>.tex
|
|
1449
1581
|
|
|
1450
1582
|
Parameters
|
|
1451
1583
|
----------
|
|
1452
|
-
path : str
|
|
1584
|
+
path : str | Path
|
|
1453
1585
|
Path to the directory which will contain the .tex file.
|
|
1454
1586
|
"""
|
|
1587
|
+
path = Path(path)
|
|
1455
1588
|
converter = LatexConverter(self, path)
|
|
1456
1589
|
converter.convert()
|
|
1457
1590
|
|
gamspy/_model_instance.py
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
import io
|
|
4
3
|
import logging
|
|
5
4
|
import os
|
|
6
5
|
import sys
|
|
@@ -66,7 +65,6 @@ from gamspy._database import (
|
|
|
66
65
|
GamsParameter,
|
|
67
66
|
GamsVariable,
|
|
68
67
|
)
|
|
69
|
-
from gamspy._options import FreezeOptions, Options
|
|
70
68
|
from gamspy.exceptions import (
|
|
71
69
|
GamspyException,
|
|
72
70
|
ValidationError,
|
|
@@ -74,7 +72,10 @@ from gamspy.exceptions import (
|
|
|
74
72
|
)
|
|
75
73
|
|
|
76
74
|
if TYPE_CHECKING:
|
|
75
|
+
import io
|
|
76
|
+
|
|
77
77
|
from gamspy import Container, Model, Parameter
|
|
78
|
+
from gamspy._options import FreezeOptions, Options
|
|
78
79
|
from gamspy._symbols.implicits import ImplicitParameter
|
|
79
80
|
|
|
80
81
|
logger = logging.getLogger("FROZEN MODEL")
|
gamspy/_options.py
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
import io
|
|
4
3
|
import os
|
|
5
4
|
import warnings
|
|
6
5
|
from pathlib import Path
|
|
@@ -11,6 +10,7 @@ from pydantic import BaseModel, ConfigDict
|
|
|
11
10
|
from gamspy.exceptions import ValidationError
|
|
12
11
|
|
|
13
12
|
if TYPE_CHECKING:
|
|
13
|
+
import io
|
|
14
14
|
from types import FrameType
|
|
15
15
|
|
|
16
16
|
from gamspy import Container
|
|
@@ -374,8 +374,8 @@ class Options(BaseModel):
|
|
|
374
374
|
report_underflow: bool | None = None
|
|
375
375
|
|
|
376
376
|
def model_post_init(self, context: Any) -> None:
|
|
377
|
-
self._extra_options: dict[str, Any] =
|
|
378
|
-
self._debug_options: dict[str, Any] =
|
|
377
|
+
self._extra_options: dict[str, Any] = {}
|
|
378
|
+
self._debug_options: dict[str, Any] = {}
|
|
379
379
|
self._solver: str | None = None
|
|
380
380
|
self._problem: str | None = None
|
|
381
381
|
self._solver_options_file: str = "0"
|
|
@@ -458,7 +458,7 @@ class Options(BaseModel):
|
|
|
458
458
|
exist_ok=True,
|
|
459
459
|
)
|
|
460
460
|
|
|
461
|
-
gams_options =
|
|
461
|
+
gams_options = {}
|
|
462
462
|
for key, value in gamspy_options.items():
|
|
463
463
|
if key not in OPTION_MAP:
|
|
464
464
|
continue
|
|
@@ -530,7 +530,7 @@ class Options(BaseModel):
|
|
|
530
530
|
if not os.path.isfile(path):
|
|
531
531
|
raise ValidationError(f"No such file in the given path: {path}")
|
|
532
532
|
|
|
533
|
-
attributes =
|
|
533
|
+
attributes = {}
|
|
534
534
|
with open(path, encoding="utf-8") as file:
|
|
535
535
|
lines = file.readlines()
|
|
536
536
|
|
|
@@ -562,7 +562,7 @@ class Options(BaseModel):
|
|
|
562
562
|
pf_file : str
|
|
563
563
|
output : io.TextIOWrapper | None, optional
|
|
564
564
|
"""
|
|
565
|
-
all_options =
|
|
565
|
+
all_options = {}
|
|
566
566
|
# Solver options
|
|
567
567
|
if self._solver is not None:
|
|
568
568
|
all_options[self._problem] = self._solver
|
gamspy/_serialization.py
CHANGED
|
@@ -5,12 +5,15 @@ import os
|
|
|
5
5
|
import shutil
|
|
6
6
|
import tempfile
|
|
7
7
|
import zipfile
|
|
8
|
-
from
|
|
8
|
+
from typing import TYPE_CHECKING
|
|
9
9
|
|
|
10
10
|
from gamspy import Container
|
|
11
11
|
from gamspy._model import ATTRIBUTE_MAP
|
|
12
12
|
from gamspy.exceptions import ValidationError
|
|
13
13
|
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from collections.abc import Sequence
|
|
16
|
+
|
|
14
17
|
|
|
15
18
|
def serialize(container: Container, path: str) -> None:
|
|
16
19
|
"""
|
|
@@ -46,15 +49,15 @@ def serialize(container: Container, path: str) -> None:
|
|
|
46
49
|
# Dump the GAMS State to disc
|
|
47
50
|
container._options._set_debug_options({"save": g00_path})
|
|
48
51
|
container._synch_with_gams()
|
|
49
|
-
container._options._set_debug_options(
|
|
52
|
+
container._options._set_debug_options({})
|
|
50
53
|
|
|
51
54
|
# Serialize symbols
|
|
52
|
-
info =
|
|
55
|
+
info = {}
|
|
53
56
|
for name, symbol in container.data.items():
|
|
54
57
|
info[name] = symbol._serialize()
|
|
55
58
|
|
|
56
59
|
# Serialize models
|
|
57
|
-
models =
|
|
60
|
+
models = {}
|
|
58
61
|
for model in container.models.values():
|
|
59
62
|
models[model.name] = model._serialize()
|
|
60
63
|
info["models"] = models
|
|
@@ -113,7 +116,7 @@ def deserialize(path: str) -> Container:
|
|
|
113
116
|
deserialized_matches: dict[str, str | Sequence[str]] = model.get(
|
|
114
117
|
"_matches", None
|
|
115
118
|
)
|
|
116
|
-
matches =
|
|
119
|
+
matches = {}
|
|
117
120
|
if deserialized_matches is not None:
|
|
118
121
|
for key, value in deserialized_matches.items():
|
|
119
122
|
if isinstance(value, str):
|
gamspy/_symbols/alias.py
CHANGED
|
@@ -2,7 +2,6 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import os
|
|
4
4
|
import threading
|
|
5
|
-
from collections.abc import Sequence
|
|
6
5
|
from typing import TYPE_CHECKING, Any
|
|
7
6
|
|
|
8
7
|
import gams.transfer as gt
|
|
@@ -20,6 +19,8 @@ from gamspy._symbols.symbol import Symbol
|
|
|
20
19
|
from gamspy.exceptions import ValidationError
|
|
21
20
|
|
|
22
21
|
if TYPE_CHECKING:
|
|
22
|
+
from collections.abc import Sequence
|
|
23
|
+
|
|
23
24
|
from gamspy import Container, Set
|
|
24
25
|
from gamspy._algebra.expression import Expression
|
|
25
26
|
|
|
@@ -72,7 +73,7 @@ class Alias(gt.Alias, operable.Operable, Symbol, SetMixin):
|
|
|
72
73
|
# gamspy attributes
|
|
73
74
|
obj.where = condition.Condition(obj)
|
|
74
75
|
obj.container._add_statement(obj)
|
|
75
|
-
obj._metadata =
|
|
76
|
+
obj._metadata = {}
|
|
76
77
|
|
|
77
78
|
return obj
|
|
78
79
|
|
|
@@ -118,7 +119,7 @@ class Alias(gt.Alias, operable.Operable, Symbol, SetMixin):
|
|
|
118
119
|
name: str | None = None,
|
|
119
120
|
alias_with: Set | Alias = None, # type: ignore
|
|
120
121
|
):
|
|
121
|
-
self._metadata: dict[str, Any] =
|
|
122
|
+
self._metadata: dict[str, Any] = {}
|
|
122
123
|
self._assignment: Expression | None = None
|
|
123
124
|
# does symbol exist
|
|
124
125
|
has_symbol = False
|
gamspy/_symbols/equation.py
CHANGED
|
@@ -27,11 +27,11 @@ from gamspy.exceptions import ValidationError
|
|
|
27
27
|
|
|
28
28
|
if TYPE_CHECKING:
|
|
29
29
|
from collections.abc import Sequence
|
|
30
|
+
from types import EllipsisType
|
|
30
31
|
|
|
31
32
|
from gamspy import Alias, Container, Set, Variable
|
|
32
33
|
from gamspy._algebra.expression import Expression
|
|
33
34
|
from gamspy._algebra.operation import Operation
|
|
34
|
-
from gamspy._types import EllipsisType
|
|
35
35
|
|
|
36
36
|
|
|
37
37
|
EQ_TYPES = ["=e=", "=l=", "=g=", "=n=", "=x=", "=b="]
|
|
@@ -45,9 +45,16 @@ IRREGULAR_EQ_MAP = {
|
|
|
45
45
|
|
|
46
46
|
class EquationType(Enum):
|
|
47
47
|
REGULAR = "regular"
|
|
48
|
+
"""Regular equations with =, >= and <= sign."""
|
|
49
|
+
|
|
48
50
|
NONBINDING = "nonbinding"
|
|
51
|
+
"""No relationship implied between left-hand side and right-hand side. This equation type is ideally suited for use in MCP models and in variational inequalities."""
|
|
52
|
+
|
|
49
53
|
EXTERNAL = "external"
|
|
54
|
+
"""Equation is defined by external programs."""
|
|
55
|
+
|
|
50
56
|
BOOLEAN = "boolean"
|
|
57
|
+
"""Boolean equations."""
|
|
51
58
|
|
|
52
59
|
@classmethod
|
|
53
60
|
def values(cls):
|
|
@@ -155,7 +162,7 @@ class Equation(gt.Equation, Symbol):
|
|
|
155
162
|
obj.where = condition.Condition(obj)
|
|
156
163
|
obj.container._add_statement(obj)
|
|
157
164
|
obj._synchronize = True
|
|
158
|
-
obj._metadata =
|
|
165
|
+
obj._metadata = {}
|
|
159
166
|
obj._winner = "python"
|
|
160
167
|
|
|
161
168
|
# create attributes
|
|
@@ -227,7 +234,7 @@ class Equation(gt.Equation, Symbol):
|
|
|
227
234
|
is_miro_output: bool = False,
|
|
228
235
|
definition_domain: list | None = None,
|
|
229
236
|
):
|
|
230
|
-
self._metadata: dict[str, Any] =
|
|
237
|
+
self._metadata: dict[str, Any] = {}
|
|
231
238
|
self._assignment: Expression | None = None
|
|
232
239
|
if is_miro_output and name is None:
|
|
233
240
|
raise ValidationError("Please specify a name for miro symbols.")
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
|
-
from collections.abc import Sequence
|
|
5
4
|
from typing import TYPE_CHECKING, Any
|
|
6
5
|
|
|
7
6
|
import gamspy._algebra.expression as expression
|
|
@@ -17,6 +16,9 @@ from gamspy.exceptions import ValidationError
|
|
|
17
16
|
from gamspy.math.matrix import permute
|
|
18
17
|
|
|
19
18
|
if TYPE_CHECKING:
|
|
19
|
+
from collections.abc import Sequence
|
|
20
|
+
from types import EllipsisType
|
|
21
|
+
|
|
20
22
|
import pandas as pd
|
|
21
23
|
|
|
22
24
|
from gamspy import (
|
|
@@ -28,7 +30,6 @@ if TYPE_CHECKING:
|
|
|
28
30
|
)
|
|
29
31
|
from gamspy._algebra.expression import Expression
|
|
30
32
|
from gamspy._algebra.operation import Operation
|
|
31
|
-
from gamspy._types import EllipsisType
|
|
32
33
|
|
|
33
34
|
logger = logging.getLogger("GAMSPy")
|
|
34
35
|
logger.setLevel(logging.WARNING)
|
|
@@ -240,7 +241,7 @@ class ImplicitParameter(ImplicitSymbol, operable.Operable):
|
|
|
240
241
|
[Set(name='i', domain=['*']), Set(name='j', domain=['*'])]
|
|
241
242
|
|
|
242
243
|
"""
|
|
243
|
-
dims =
|
|
244
|
+
dims = list(range(len(self.domain)))
|
|
244
245
|
if len(dims) < 2:
|
|
245
246
|
raise ValidationError(
|
|
246
247
|
"Parameter must contain at least 2 dimensions to transpose"
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from collections.abc import Sequence
|
|
4
3
|
from typing import TYPE_CHECKING
|
|
5
4
|
|
|
6
5
|
import gamspy._algebra.expression as expression
|
|
@@ -12,11 +11,13 @@ from gamspy._symbols.implicits.implicit_symbol import ImplicitSymbol
|
|
|
12
11
|
from gamspy.exceptions import ValidationError
|
|
13
12
|
|
|
14
13
|
if TYPE_CHECKING:
|
|
14
|
+
from collections.abc import Sequence
|
|
15
|
+
from types import EllipsisType
|
|
16
|
+
|
|
15
17
|
import pandas as pd
|
|
16
18
|
|
|
17
19
|
from gamspy import Alias, Set
|
|
18
20
|
from gamspy._algebra.expression import Expression
|
|
19
|
-
from gamspy._types import EllipsisType
|
|
20
21
|
|
|
21
22
|
|
|
22
23
|
class ImplicitSet(ImplicitSymbol, operable.Operable):
|
|
@@ -76,7 +76,7 @@ class ImplicitSymbol(ABC):
|
|
|
76
76
|
self.domain = domain
|
|
77
77
|
|
|
78
78
|
scalars.extend(parent_scalar_domains)
|
|
79
|
-
scalars =
|
|
79
|
+
scalars = sorted(scalars, key=lambda k: k[0])
|
|
80
80
|
self._scalar_domains = scalars
|
|
81
81
|
|
|
82
82
|
def fix_permutation(self):
|
|
@@ -147,7 +147,7 @@ class ImplicitSymbol(ABC):
|
|
|
147
147
|
raise ValidationError("Sum operation is not possible on scalar parameters.")
|
|
148
148
|
|
|
149
149
|
if not indices:
|
|
150
|
-
|
|
150
|
+
return gp.Sum(self.domain, self[self.domain])
|
|
151
151
|
|
|
152
152
|
return gp.Sum(indices, self[self.domain])
|
|
153
153
|
|
|
@@ -179,7 +179,7 @@ class ImplicitSymbol(ABC):
|
|
|
179
179
|
)
|
|
180
180
|
|
|
181
181
|
if not indices:
|
|
182
|
-
|
|
182
|
+
return gp.Product(self.domain, self[self.domain])
|
|
183
183
|
|
|
184
184
|
return gp.Product(indices, self[self.domain])
|
|
185
185
|
|
|
@@ -211,7 +211,7 @@ class ImplicitSymbol(ABC):
|
|
|
211
211
|
)
|
|
212
212
|
|
|
213
213
|
if not indices:
|
|
214
|
-
|
|
214
|
+
return gp.Smin(self.domain, self[self.domain])
|
|
215
215
|
|
|
216
216
|
return gp.Smin(indices, self[self.domain])
|
|
217
217
|
|
|
@@ -243,7 +243,7 @@ class ImplicitSymbol(ABC):
|
|
|
243
243
|
)
|
|
244
244
|
|
|
245
245
|
if not indices:
|
|
246
|
-
|
|
246
|
+
return gp.Smax(self.domain, self[self.domain])
|
|
247
247
|
|
|
248
248
|
return gp.Smax(indices, self[self.domain])
|
|
249
249
|
|
|
@@ -275,7 +275,7 @@ class ImplicitSymbol(ABC):
|
|
|
275
275
|
)
|
|
276
276
|
|
|
277
277
|
if not indices:
|
|
278
|
-
|
|
278
|
+
return gp.Sand(self.domain, self[self.domain])
|
|
279
279
|
|
|
280
280
|
return gp.Sand(indices, self[self.domain])
|
|
281
281
|
|
|
@@ -305,6 +305,6 @@ class ImplicitSymbol(ABC):
|
|
|
305
305
|
raise ValidationError("Sor operation is not possible on scalar parameters.")
|
|
306
306
|
|
|
307
307
|
if not indices:
|
|
308
|
-
|
|
308
|
+
return gp.Sor(self.domain, self[self.domain])
|
|
309
309
|
|
|
310
310
|
return gp.Sor(indices, self[self.domain])
|