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.
Files changed (44) hide show
  1. gamspy/__init__.py +1 -1
  2. gamspy/_algebra/expression.py +12 -14
  3. gamspy/_algebra/operation.py +3 -2
  4. gamspy/_cli/install.py +4 -2
  5. gamspy/_cli/show.py +4 -1
  6. gamspy/_cli/uninstall.py +4 -2
  7. gamspy/_config.py +1 -1
  8. gamspy/_container.py +27 -7
  9. gamspy/_convert.py +18 -17
  10. gamspy/_database.py +1 -1
  11. gamspy/_miro.py +1 -1
  12. gamspy/_model.py +146 -13
  13. gamspy/_model_instance.py +3 -2
  14. gamspy/_options.py +6 -6
  15. gamspy/_serialization.py +8 -5
  16. gamspy/_symbols/alias.py +4 -3
  17. gamspy/_symbols/equation.py +10 -3
  18. gamspy/_symbols/implicits/implicit_parameter.py +4 -3
  19. gamspy/_symbols/implicits/implicit_set.py +3 -2
  20. gamspy/_symbols/implicits/implicit_symbol.py +7 -7
  21. gamspy/_symbols/implicits/implicit_variable.py +4 -3
  22. gamspy/_symbols/parameter.py +6 -5
  23. gamspy/_symbols/set.py +4 -3
  24. gamspy/_symbols/universe_alias.py +1 -1
  25. gamspy/_symbols/variable.py +23 -5
  26. gamspy/_types.py +0 -1
  27. gamspy/_validation.py +5 -10
  28. gamspy/exceptions.py +1 -1
  29. gamspy/formulations/__init__.py +2 -0
  30. gamspy/formulations/nn/maxpool2d.py +5 -1
  31. gamspy/formulations/nn/minpool2d.py +5 -1
  32. gamspy/formulations/nn/torch_sequential.py +80 -8
  33. gamspy/formulations/result.py +119 -0
  34. gamspy/math/__init__.py +4 -0
  35. gamspy/math/activation.py +195 -8
  36. gamspy/math/matrix.py +2 -2
  37. gamspy/math/misc.py +1 -1
  38. gamspy/utils.py +61 -47
  39. {gamspy-1.17.0.dist-info → gamspy-1.17.2.dist-info}/METADATA +3 -2
  40. {gamspy-1.17.0.dist-info → gamspy-1.17.2.dist-info}/RECORD +44 -43
  41. {gamspy-1.17.0.dist-info → gamspy-1.17.2.dist-info}/WHEEL +0 -0
  42. {gamspy-1.17.0.dist-info → gamspy-1.17.2.dist-info}/entry_points.txt +0 -0
  43. {gamspy-1.17.0.dist-info → gamspy-1.17.2.dist-info}/licenses/LICENSE +0 -0
  44. {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 = 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
- solve_string = f"solve {self.name} using {self.problem}"
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
- solve_string += f" {self.sense}"
1064
+ solve_statement.append(str(self.sense))
935
1065
 
936
1066
  if self._objective_variable is not None:
937
- solve_string += f" {self._objective_variable.gamsRepr()}"
1067
+ solve_statement.append(self._objective_variable.gamsRepr())
938
1068
 
939
- return solve_string
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 += [equation for equation in key]
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] = dict()
378
- self._debug_options: dict[str, Any] = dict()
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 = dict()
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 = dict()
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 = dict()
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 collections.abc import Sequence
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(dict())
52
+ container._options._set_debug_options({})
50
53
 
51
54
  # Serialize symbols
52
- info = dict()
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 = dict()
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 = dict()
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 = dict()
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] = dict()
122
+ self._metadata: dict[str, Any] = {}
122
123
  self._assignment: Expression | None = None
123
124
  # does symbol exist
124
125
  has_symbol = False
@@ -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 = dict()
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] = dict()
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 = [x for x in range(len(self.domain))]
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 = list(sorted(scalars, key=lambda k: k[0]))
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
- indices = self.domain
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
- indices = self.domain
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
- indices = self.domain
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
- indices = self.domain
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
- indices = self.domain
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
- indices = self.domain
308
+ return gp.Sor(self.domain, self[self.domain])
309
309
 
310
310
  return gp.Sor(indices, self[self.domain])