gamspy 1.15.0__tar.gz → 1.15.1__tar.gz

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 (95) hide show
  1. {gamspy-1.15.0 → gamspy-1.15.1}/PKG-INFO +3 -3
  2. {gamspy-1.15.0 → gamspy-1.15.1}/pyproject.toml +3 -3
  3. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy/_algebra/expression.py +3 -0
  4. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy/_cli/install.py +1 -1
  5. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy/_cli/retrieve.py +13 -6
  6. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy/_config.py +5 -0
  7. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy/_container.py +1 -1
  8. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy/_convert.py +1 -1
  9. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy/_miro.py +5 -1
  10. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy/_symbols/alias.py +2 -2
  11. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy/_symbols/equation.py +40 -8
  12. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy/_symbols/implicits/implicit_equation.py +18 -4
  13. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy/_symbols/implicits/implicit_parameter.py +47 -51
  14. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy/_symbols/implicits/implicit_set.py +16 -5
  15. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy/_symbols/implicits/implicit_variable.py +17 -4
  16. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy/_symbols/parameter.py +9 -9
  17. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy/_symbols/set.py +8 -8
  18. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy/_symbols/symbol.py +1 -1
  19. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy/_symbols/variable.py +8 -8
  20. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy/_validation.py +44 -8
  21. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy/formulations/__init__.py +8 -1
  22. gamspy-1.15.1/src/gamspy/formulations/ml/__init__.py +11 -0
  23. gamspy-1.15.1/src/gamspy/formulations/ml/gradient_boosting.py +186 -0
  24. gamspy-1.15.1/src/gamspy/formulations/ml/random_forest.py +168 -0
  25. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy/formulations/ml/regression_tree.py +223 -122
  26. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy/formulations/nn/avgpool2d.py +1 -1
  27. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy/formulations/nn/conv1d.py +17 -4
  28. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy/formulations/nn/conv2d.py +18 -4
  29. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy/formulations/nn/linear.py +17 -4
  30. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy/formulations/nn/mpool2d.py +1 -1
  31. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy/formulations/piecewise.py +4 -6
  32. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy/formulations/shape.py +1 -1
  33. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy/math/activation.py +14 -0
  34. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy.egg-info/PKG-INFO +3 -3
  35. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy.egg-info/SOURCES.txt +3 -1
  36. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy.egg-info/requires.txt +2 -2
  37. {gamspy-1.15.0 → gamspy-1.15.1}/tests/test_gamspy.py +1 -1
  38. gamspy-1.15.0/src/gamspy/formulations/ml/__init__.py +0 -4
  39. {gamspy-1.15.0 → gamspy-1.15.1}/LICENSE +0 -0
  40. {gamspy-1.15.0 → gamspy-1.15.1}/README.md +0 -0
  41. {gamspy-1.15.0 → gamspy-1.15.1}/README_PYPI.md +0 -0
  42. {gamspy-1.15.0 → gamspy-1.15.1}/setup.cfg +0 -0
  43. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy/__init__.py +0 -0
  44. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy/__main__.py +0 -0
  45. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy/_algebra/__init__.py +0 -0
  46. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy/_algebra/condition.py +0 -0
  47. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy/_algebra/domain.py +0 -0
  48. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy/_algebra/number.py +0 -0
  49. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy/_algebra/operable.py +0 -0
  50. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy/_algebra/operation.py +0 -0
  51. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy/_backend/__init__.py +0 -0
  52. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy/_backend/backend.py +0 -0
  53. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy/_backend/engine.py +0 -0
  54. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy/_backend/local.py +0 -0
  55. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy/_backend/neos.py +0 -0
  56. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy/_cli/__init__.py +0 -0
  57. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy/_cli/cli.py +0 -0
  58. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy/_cli/gdx.py +0 -0
  59. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy/_cli/list.py +0 -0
  60. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy/_cli/probe.py +0 -0
  61. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy/_cli/run.py +0 -0
  62. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy/_cli/show.py +0 -0
  63. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy/_cli/uninstall.py +0 -0
  64. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy/_cli/util.py +0 -0
  65. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy/_database.py +0 -0
  66. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy/_extrinsic.py +0 -0
  67. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy/_model.py +0 -0
  68. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy/_model_instance.py +0 -0
  69. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy/_options.py +0 -0
  70. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy/_serialization.py +0 -0
  71. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy/_symbols/__init__.py +0 -0
  72. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy/_symbols/implicits/__init__.py +0 -0
  73. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy/_symbols/implicits/implicit_symbol.py +0 -0
  74. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy/_symbols/universe_alias.py +0 -0
  75. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy/_types.py +0 -0
  76. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy/_workspace.py +0 -0
  77. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy/exceptions.py +0 -0
  78. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy/formulations/ml/decision_tree_struct.py +0 -0
  79. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy/formulations/nn/__init__.py +0 -0
  80. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy/formulations/nn/maxpool2d.py +0 -0
  81. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy/formulations/nn/minpool2d.py +0 -0
  82. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy/formulations/nn/torch_sequential.py +0 -0
  83. {gamspy-1.15.0/src/gamspy/formulations/nn → gamspy-1.15.1/src/gamspy/formulations}/utils.py +0 -0
  84. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy/math/__init__.py +0 -0
  85. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy/math/log_power.py +0 -0
  86. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy/math/matrix.py +0 -0
  87. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy/math/misc.py +0 -0
  88. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy/math/probability.py +0 -0
  89. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy/math/trigonometric.py +0 -0
  90. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy/py.typed +0 -0
  91. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy/utils.py +0 -0
  92. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy/version.py +0 -0
  93. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy.egg-info/dependency_links.txt +0 -0
  94. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy.egg-info/entry_points.txt +0 -0
  95. {gamspy-1.15.0 → gamspy-1.15.1}/src/gamspy.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gamspy
3
- Version: 1.15.0
3
+ Version: 1.15.1
4
4
  Summary: Python-based algebraic modeling interface to GAMS
5
5
  Author-email: GAMS Development Corporation <support@gams.com>
6
6
  Project-URL: homepage, https://gams.com/sales/gamspy_facts/
@@ -31,8 +31,8 @@ Classifier: Operating System :: Microsoft :: Windows
31
31
  Requires-Python: >=3.9
32
32
  Description-Content-Type: text/markdown
33
33
  License-File: LICENSE
34
- Requires-Dist: gamsapi[transfer]==50.4.0
35
- Requires-Dist: gamspy_base==50.4.0
34
+ Requires-Dist: gamsapi[transfer]==50.4.1
35
+ Requires-Dist: gamspy_base==50.4.1
36
36
  Requires-Dist: pydantic>=2.0
37
37
  Requires-Dist: certifi>=2022.09.14
38
38
  Requires-Dist: urllib3>=2.0.7
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "gamspy"
7
- version = "1.15.0"
7
+ version = "1.15.1"
8
8
  authors = [
9
9
  { name = "GAMS Development Corporation", email = "support@gams.com" },
10
10
  ]
@@ -36,8 +36,8 @@ classifiers = [
36
36
  "Operating System :: Microsoft :: Windows",
37
37
  ]
38
38
  dependencies = [
39
- "gamsapi[transfer] == 50.4.0",
40
- "gamspy_base == 50.4.0",
39
+ "gamsapi[transfer] == 50.4.1",
40
+ "gamspy_base == 50.4.1",
41
41
  "pydantic >= 2.0",
42
42
  "certifi >= 2022.09.14",
43
43
  "urllib3 >= 2.0.7",
@@ -650,6 +650,9 @@ class Expression(operable.Operable):
650
650
 
651
651
  if isinstance(node, Symbol):
652
652
  if node.name not in symbols:
653
+ if type(node) is gp_syms.Alias:
654
+ symbols.append(node.alias_with.name)
655
+
653
656
  symbols.append(node.name)
654
657
  stack += node.domain
655
658
  node = None
@@ -261,7 +261,7 @@ def solver(
261
261
  _ = subprocess.run(command, check=True, encoding="utf-8", stderr=subprocess.PIPE)
262
262
  except subprocess.CalledProcessError as e:
263
263
  raise GamspyException(
264
- f"Could not install gamspy-{solver_name}. Please check your internet connection. If it's not related to your internet connection, PyPI servers might be down. Please retry it later. Here is the error message of pip:\n\n{e.stderr.decode('utf-8')}"
264
+ f"Could not install gamspy-{solver_name}. Please check your internet connection. If it's not related to your internet connection, PyPI servers might be down. Please retry it later. Here is the error message of pip:\n\n{e.stderr}"
265
265
  ) from e
266
266
  else:
267
267
  try:
@@ -2,6 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  import os
4
4
  import subprocess
5
+ from typing import Optional
5
6
 
6
7
  import typer
7
8
 
@@ -37,6 +38,7 @@ def license(
37
38
  "-o",
38
39
  help="Output path for the license file."
39
40
  ),
41
+ checkout_duration: Optional[int] = typer.Option(None, "--checkout-duration", "-c", help="Specifies a duration in hours to checkout a session."),
40
42
  ) -> None:
41
43
  if input is None or not os.path.isfile(input):
42
44
  raise ValidationError(
@@ -49,13 +51,18 @@ def license(
49
51
  )
50
52
 
51
53
  gamspy_base_dir = utils._get_gamspy_base_directory()
54
+ command = [
55
+ os.path.join(gamspy_base_dir, "gamsgetkey"),
56
+ access_code,
57
+ "-i",
58
+ input,
59
+ ]
60
+ if checkout_duration:
61
+ command.append("-c")
62
+ command.append(str(checkout_duration))
63
+
52
64
  process = subprocess.run(
53
- [
54
- os.path.join(gamspy_base_dir, "gamsgetkey"),
55
- access_code,
56
- "-i",
57
- input,
58
- ],
65
+ command,
59
66
  text=True,
60
67
  capture_output=True,
61
68
  )
@@ -55,6 +55,10 @@ def _set_default_options() -> None:
55
55
  strategy = int(os.getenv("GAMSPY_DROP_DOMAIN_VIOLATIONS", 0))
56
56
  configuration["DROP_DOMAIN_VIOLATIONS"] = strategy
57
57
 
58
+ # Do not allow ambiguity in MCP, EMP, MPEC models by default
59
+ ambiguity = os.getenv("GAMSPY_ALLOW_AMBIGUOUS_EQUATIONS", "auto")
60
+ configuration["ALLOW_AMBIGUOUS_EQUATIONS"] = ambiguity
61
+
58
62
 
59
63
  def set_options(
60
64
  options: dict[
@@ -67,6 +71,7 @@ def set_options(
67
71
  "ASSUME_VARIABLE_SUFFIX",
68
72
  "USE_PY_VAR_NAME",
69
73
  "DROP_DOMAIN_VIOLATIONS",
74
+ "ALLOW_AMBIGUOUS_EQUATIONS",
70
75
  ],
71
76
  Any,
72
77
  ],
@@ -580,7 +580,7 @@ class Container(gt.Container):
580
580
  modified_names = []
581
581
 
582
582
  for name, symbol in self:
583
- if symbol.modified:
583
+ if symbol._modified:
584
584
  if (
585
585
  type(symbol) is gp.Alias
586
586
  and symbol.alias_with.name not in modified_names # type: ignore
@@ -490,7 +490,7 @@ class GamsConverter:
490
490
  file.write(gams_string)
491
491
 
492
492
  logger.info(
493
- f"GAMS (.gms) file has been generated under {os.path.join(self.path, self.model.name + '.gms')}"
493
+ f"GAMS (.gms) file has been generated under {self.gms_path}"
494
494
  )
495
495
 
496
496
 
@@ -45,7 +45,11 @@ def load_miro_symbol_records(container: Container):
45
45
  for name in names:
46
46
  symbol = container[name]
47
47
  symbol._already_loaded = True
48
- if isinstance(symbol, gp.Parameter) and symbol._is_miro_table:
48
+ if (
49
+ isinstance(symbol, gp.Parameter)
50
+ and symbol._is_miro_table
51
+ and symbol._records is not None
52
+ ):
49
53
  symbol._records.columns = symbol.domain_names + ["value"]
50
54
 
51
55
  # Load records of miro output symbols
@@ -134,7 +134,7 @@ class Alias(gt.Alias, operable.Operable, Symbol, SetMixin):
134
134
  # reset some properties
135
135
  self._requires_state_check = True
136
136
  self.container._requires_state_check = True
137
- self.modified = True
137
+ self._modified = True
138
138
  self.alias_with = alias_with
139
139
  else:
140
140
  if container is None:
@@ -157,7 +157,7 @@ class Alias(gt.Alias, operable.Operable, Symbol, SetMixin):
157
157
  self.where = condition.Condition(self)
158
158
  self.container._add_statement(self)
159
159
 
160
- self.modified = False
160
+ self._modified = False
161
161
  self.container._synch_with_gams()
162
162
 
163
163
  def _serialize(self) -> dict:
@@ -18,6 +18,7 @@ from gams.transfer._internals import (
18
18
  import gamspy as gp
19
19
  import gamspy._algebra.condition as condition
20
20
  import gamspy._algebra.expression as expression
21
+ import gamspy._algebra.operable as operable
21
22
  import gamspy._symbols.implicits as implicits
22
23
  import gamspy._validation as validation
23
24
  import gamspy.utils as utils
@@ -273,7 +274,7 @@ class Equation(gt.Equation, Symbol):
273
274
  " domains are equal"
274
275
  )
275
276
 
276
- if self.domain_forwarding != domain_forwarding:
277
+ if self._domain_forwarding != domain_forwarding:
277
278
  raise ValueError(
278
279
  "Cannot overwrite symbol in container unless"
279
280
  " 'domain_forwarding' is left unchanged"
@@ -287,8 +288,8 @@ class Equation(gt.Equation, Symbol):
287
288
 
288
289
  previous_state = self.container._options.miro_protect
289
290
  self.container._options.miro_protect = False
290
- self.records = None
291
- self.modified = True
291
+ self._records = None
292
+ self._modified = True
292
293
  self._init_definition(definition)
293
294
 
294
295
  # only set records if records are provided
@@ -360,14 +361,14 @@ class Equation(gt.Equation, Symbol):
360
361
  self.setRecords(records, uels_on_axes=uels_on_axes)
361
362
  else:
362
363
  if not self._is_miro_output:
363
- self.modified = False
364
+ self._modified = False
364
365
  self.container._synch_with_gams()
365
366
 
366
367
  container._options.miro_protect = previous_state
367
368
 
368
369
  def _serialize(self) -> dict:
369
370
  info = {
370
- "_domain_forwarding": self.domain_forwarding,
371
+ "_domain_forwarding": self._domain_forwarding,
371
372
  "_is_miro_output": self._is_miro_output,
372
373
  "_metadata": self._metadata,
373
374
  "_synchronize": self._synchronize,
@@ -554,6 +555,37 @@ class Equation(gt.Equation, Symbol):
554
555
  self.container._add_statement(statement)
555
556
  self._definition = statement
556
557
 
558
+ def _check_ambiguity(self) -> None:
559
+ """Ambiguity check for MCP, EMP, MPEC models. See #610"""
560
+ # Looks for =e=, =l= and =g= in an equation definition
561
+ # with a stack based inorder traversal algorithm (O(N)).
562
+ stack = []
563
+
564
+ assert self._definition is not None
565
+ node = self._definition.right
566
+ while True:
567
+ if node is not None:
568
+ stack.append(node)
569
+ node = getattr(node, "left", None) # type: ignore
570
+ elif stack:
571
+ node = stack.pop()
572
+ if (
573
+ isinstance(node, expression.Expression)
574
+ and node.operator in {"=e=", "=l=", "=g=", "=x=", "=n="}
575
+ and not isinstance(node.right, operable.Operable)
576
+ ):
577
+ raise ValidationError(
578
+ f"Definition of `{self.name}` is ambigiuous. Please "
579
+ "use gp.Number for numeric values or disable ambiguity "
580
+ "check via gp.set_options({'ALLOW_AMBIGUOUS_EQUATIONS': 'no'}). "
581
+ "Using numeric values in equations without gp.Number can result in "
582
+ f"different order than expected. Print `{self.name}.getDefinition()` to "
583
+ "make sure that the equation definition is as expected."
584
+ )
585
+ node = getattr(node, "right", None)
586
+ else:
587
+ break # pragma: no cover
588
+
557
589
  @property
558
590
  def l(self): # noqa: E741, E743
559
591
  """
@@ -1000,12 +1032,12 @@ class Equation(gt.Equation, Symbol):
1000
1032
  self._records = records
1001
1033
 
1002
1034
  self._requires_state_check = True
1003
- self.modified = True
1035
+ self._modified = True
1004
1036
 
1005
1037
  self.container._requires_state_check = True
1006
1038
  self.container.modified = True
1007
1039
 
1008
- if self._records is not None and self.domain_forwarding:
1040
+ if self._records is not None and self._domain_forwarding:
1009
1041
  self._domainForwarding()
1010
1042
 
1011
1043
  # reset state check flags for all symbols in the container
@@ -1159,7 +1191,7 @@ class Equation(gt.Equation, Symbol):
1159
1191
  output = f"Equation {self.name}"
1160
1192
 
1161
1193
  if self.domain:
1162
- output += self._get_domain_str(self.domain_forwarding)
1194
+ output += self._get_domain_str(self._domain_forwarding)
1163
1195
 
1164
1196
  if self.description:
1165
1197
  output += ' "' + self.description + '"'
@@ -2,9 +2,11 @@ from __future__ import annotations
2
2
 
3
3
  from typing import TYPE_CHECKING
4
4
 
5
+ import gamspy._symbols as syms
5
6
  import gamspy._symbols.alias as alias
6
7
  import gamspy._symbols.implicits as implicits
7
8
  import gamspy._symbols.set as gams_set
9
+ import gamspy.utils as utils
8
10
  from gamspy._symbols.implicits.implicit_symbol import ImplicitSymbol
9
11
 
10
12
  if TYPE_CHECKING:
@@ -141,10 +143,22 @@ class ImplicitEquation(ImplicitSymbol):
141
143
  if self.parent.records is None:
142
144
  return None
143
145
 
144
- recs = self.parent.records
145
- for idx, literal in self._scalar_domains:
146
- column_name = recs.columns[idx]
147
- recs = recs[recs[column_name] == literal]
146
+ temp_name = "ie" + utils._get_unique_name()
147
+ temp_param = syms.Parameter._constructor_bypass(
148
+ self.container, temp_name, self.parent.domain
149
+ )
150
+ domain = list(self.domain)
151
+ for i, d in self._scalar_domains:
152
+ domain.insert(i, d)
153
+
154
+ temp_param[domain] = self.l
155
+ del self.container.data[temp_name]
156
+
157
+ recs = temp_param.records
158
+ if recs is not None:
159
+ columns = recs.columns.to_list()
160
+ columns[columns.index("value")] = "level"
161
+ recs.columns = columns
148
162
 
149
163
  return recs
150
164
 
@@ -1,13 +1,14 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import logging
4
- from collections.abc import Iterable
4
+ from collections.abc import Sequence
5
5
  from typing import TYPE_CHECKING, Any
6
6
 
7
7
  import gamspy._algebra.expression as expression
8
8
  import gamspy._algebra.operable as operable
9
9
  import gamspy._algebra.operation as operation
10
10
  import gamspy._symbols as syms
11
+ import gamspy._symbols.implicits as implicits
11
12
  import gamspy._validation as validation
12
13
  import gamspy.utils as utils
13
14
  from gamspy._symbols.implicits.implicit_symbol import ImplicitSymbol
@@ -26,6 +27,7 @@ if TYPE_CHECKING:
26
27
  )
27
28
  from gamspy._algebra.expression import Expression
28
29
  from gamspy._algebra.operation import Operation
30
+ from gamspy._types import EllipsisType
29
31
 
30
32
  logger = logging.getLogger("GAMSPy")
31
33
  logger.setLevel(logging.WARNING)
@@ -41,6 +43,11 @@ ATTR_MAPPING = {
41
43
  "lo": "lower",
42
44
  "up": "upper",
43
45
  "scale": "scale",
46
+ "range": "range",
47
+ "slack": "slack",
48
+ "slacklo": "slacklo",
49
+ "slackup": "slackup",
50
+ "infeas": "infeas",
44
51
  }
45
52
 
46
53
  SET_ATTR_MAPPING = {
@@ -81,7 +88,10 @@ class ImplicitParameter(ImplicitSymbol, operable.Operable):
81
88
  self._records = records
82
89
  self._assignment = None
83
90
 
84
- def __getitem__(self, indices: Iterable | str) -> ImplicitParameter:
91
+ def __getitem__(
92
+ self,
93
+ indices: EllipsisType | slice | Sequence | str | implicits.ImplicitSet,
94
+ ) -> ImplicitParameter:
85
95
  domain = validation.validate_domain(self, indices)
86
96
 
87
97
  return ImplicitParameter(
@@ -94,7 +104,7 @@ class ImplicitParameter(ImplicitSymbol, operable.Operable):
94
104
 
95
105
  def __setitem__(
96
106
  self,
97
- indices: Iterable | str,
107
+ indices: EllipsisType | slice | Sequence | str | implicits.ImplicitSet,
98
108
  rhs: Expression | Operation | ImplicitParameter | int | float,
99
109
  ) -> None:
100
110
  if (
@@ -146,32 +156,6 @@ class ImplicitParameter(ImplicitSymbol, operable.Operable):
146
156
  def __repr__(self) -> str:
147
157
  return f"ImplicitParameter(parent={self.parent}, name='{self.name}', domain={self.domain}, permutation={self.permutation}), parent_scalar_domains={self.parent_scalar_domains})"
148
158
 
149
- def _add_new_attr_column(
150
- self, extension: str, recs: pd.DataFrame
151
- ) -> pd.DataFrame:
152
- parent_recs: pd.DataFrame = self.parent.records
153
- if extension == "range":
154
- recs[extension] = parent_recs["lower"] - parent_recs["upper"]
155
- elif extension == "slacklo":
156
- recs[extension] = (
157
- parent_recs["level"] - parent_recs["lower"]
158
- ).clip(0)
159
- elif extension == "slackup":
160
- recs[extension] = (
161
- parent_recs["upper"] - parent_recs["level"]
162
- ).clip(0)
163
- elif extension == "slack":
164
- slacklo = (parent_recs["level"] - parent_recs["lower"]).clip(0)
165
- slackup = (parent_recs["upper"] - parent_recs["level"]).clip(0)
166
- recs[extension] = slacklo.combine(slackup, min)
167
- elif extension == "infeas":
168
- distance_to_lower = parent_recs["lower"] - parent_recs["level"]
169
- distance_to_upper = parent_recs["level"] - parent_recs["upper"]
170
- max_distance = distance_to_lower.combine(distance_to_upper, max)
171
- recs[extension] = max_distance.clip(lower=0)
172
-
173
- return recs
174
-
175
159
  @property
176
160
  def records(self) -> pd.DataFrame | float | None:
177
161
  if self.parent.records is None:
@@ -187,37 +171,49 @@ class ImplicitParameter(ImplicitSymbol, operable.Operable):
187
171
  del self.container.data[temp_name]
188
172
  return temp_param.records
189
173
  elif isinstance(self.parent, syms.Parameter):
190
- recs = self.parent.records
191
- for idx, literal in self._scalar_domains:
192
- column_name = recs.columns[idx]
193
- recs = recs[recs[column_name] == literal]
194
-
195
- if all(
196
- isinstance(elem, str) and elem != "*" for elem in self.domain
197
- ):
174
+ temp_name = "p" + utils._get_unique_name()
175
+ temp_param = syms.Parameter._constructor_bypass(
176
+ self.container, temp_name, self.parent.domain
177
+ )
178
+ domain = list(self.domain)
179
+ for i, d in self._scalar_domains:
180
+ domain.insert(i, d)
181
+
182
+ temp_param[domain] = self[...]
183
+ del self.container.data[temp_name]
184
+
185
+ recs = temp_param.records
186
+ if len(recs) == 1:
198
187
  return float(recs["value"].squeeze())
199
188
 
200
189
  return recs
201
190
  elif isinstance(self.parent, (syms.Variable, syms.Equation)):
202
191
  extension = self.name.split(".")[-1]
203
- column_names: list[str] = self.parent.domain_labels
204
-
205
- if extension in ATTR_MAPPING:
192
+ if self.parent.dimension == 0:
206
193
  extension = ATTR_MAPPING[extension]
207
- column_names.append(extension)
194
+ return float(self.parent.records[extension].squeeze())
195
+
196
+ temp_name = "ip" + utils._get_unique_name()
197
+ temp_param = syms.Parameter._constructor_bypass(
198
+ self.container, temp_name, self.parent.domain
199
+ )
200
+ domain = list(self.domain)
201
+ for i, d in self._scalar_domains:
202
+ domain.insert(i, d)
203
+
204
+ temp_param[domain] = self
205
+ del self.container.data[temp_name]
208
206
 
209
- recs = self.parent.records[column_names].copy()
210
- if extension not in ATTR_MAPPING:
211
- # eq.range, eq.slacklo, eq.slackup, eq.slack, eq.infeas
212
- recs = self._add_new_attr_column(extension, recs)
207
+ recs = temp_param.records
208
+ if recs is None:
209
+ return recs
213
210
 
214
- for idx, literal in self._scalar_domains:
215
- column_name = recs.columns[idx]
216
- recs = recs[recs[column_name] == literal]
211
+ extension = ATTR_MAPPING[extension]
212
+ columns = recs.columns.to_list()
213
+ columns[columns.index("value")] = extension
214
+ recs.columns = columns
217
215
 
218
- if all(
219
- isinstance(elem, str) and elem != "*" for elem in self.domain
220
- ):
216
+ if len(recs) == 1:
221
217
  return float(recs[extension].squeeze())
222
218
 
223
219
  return recs
@@ -9,6 +9,7 @@ import gamspy._symbols as syms
9
9
  import gamspy._validation as validation
10
10
  import gamspy.utils as utils
11
11
  from gamspy._symbols.implicits.implicit_symbol import ImplicitSymbol
12
+ from gamspy.exceptions import ValidationError
12
13
 
13
14
  if TYPE_CHECKING:
14
15
  import pandas as pd
@@ -67,12 +68,22 @@ class ImplicitSet(ImplicitSymbol, operable.Operable):
67
68
  if self.parent.records is None:
68
69
  return None
69
70
 
70
- recs = self.parent.records
71
- for idx, literal in self._scalar_domains:
72
- column_name = recs.columns[idx]
73
- recs = recs[recs[column_name] == literal]
71
+ if self.extension is not None:
72
+ raise ValidationError(
73
+ ".records is not allowed for lag/lead operations."
74
+ )
75
+
76
+ temp_name = "is" + utils._get_unique_name()
77
+ temp_param = syms.Set._constructor_bypass(
78
+ self.container, temp_name, self.parent.domain
79
+ )
80
+ domain = list(self.domain)
81
+ for i, d in self._scalar_domains:
82
+ domain.insert(i, d)
74
83
 
75
- return recs
84
+ temp_param[domain] = self
85
+ del self.container.data[temp_name]
86
+ return temp_param.records
76
87
 
77
88
  def latexRepr(self):
78
89
  name = self.name.replace("_", "\\_")
@@ -5,6 +5,7 @@ from typing import TYPE_CHECKING
5
5
 
6
6
  import gamspy._algebra.expression as expression
7
7
  import gamspy._algebra.operable as operable
8
+ import gamspy._symbols as syms
8
9
  import gamspy._symbols.implicits as implicits
9
10
  import gamspy._validation as validation
10
11
  import gamspy.utils as utils
@@ -217,10 +218,22 @@ class ImplicitVariable(ImplicitSymbol, operable.Operable):
217
218
  if self.parent.records is None:
218
219
  return None
219
220
 
220
- recs = self.parent.records
221
- for idx, literal in self._scalar_domains:
222
- column_name = recs.columns[idx]
223
- recs = recs[recs[column_name] == literal]
221
+ temp_name = "iv" + utils._get_unique_name()
222
+ temp_param = syms.Parameter._constructor_bypass(
223
+ self.container, temp_name, self.parent.domain
224
+ )
225
+ domain = list(self.domain)
226
+ for i, d in self._scalar_domains:
227
+ domain.insert(i, d)
228
+
229
+ temp_param[domain] = self.l
230
+ del self.container.data[temp_name]
231
+
232
+ recs = temp_param.records
233
+ if recs is not None:
234
+ columns = recs.columns.to_list()
235
+ columns[columns.index("value")] = "level"
236
+ recs.columns = columns
224
237
 
225
238
  return recs
226
239
 
@@ -234,7 +234,7 @@ class Parameter(gt.Parameter, operable.Operable, Symbol):
234
234
  " domains are equal"
235
235
  )
236
236
 
237
- if self.domain_forwarding != domain_forwarding:
237
+ if self._domain_forwarding != domain_forwarding:
238
238
  raise ValueError(
239
239
  "Cannot overwrite symbol in container unless"
240
240
  " 'domain_forwarding' is left unchanged"
@@ -248,8 +248,8 @@ class Parameter(gt.Parameter, operable.Operable, Symbol):
248
248
 
249
249
  previous_state = self.container._options.miro_protect
250
250
  self.container._options.miro_protect = False
251
- self.records = None
252
- self.modified = True
251
+ self._records = None
252
+ self._modified = True
253
253
 
254
254
  # only set records if records are provided
255
255
  if records is not None:
@@ -301,7 +301,7 @@ class Parameter(gt.Parameter, operable.Operable, Symbol):
301
301
  if records is not None:
302
302
  super().setRecords(records, uels_on_axes=uels_on_axes)
303
303
  if self.dimension == 0 and not self._is_miro_symbol:
304
- self.modified = False
304
+ self._modified = False
305
305
 
306
306
  if gp.get_option("DROP_DOMAIN_VIOLATIONS"):
307
307
  if self.hasDomainViolations():
@@ -311,7 +311,7 @@ class Parameter(gt.Parameter, operable.Operable, Symbol):
311
311
  self._domain_violations = None
312
312
  else:
313
313
  if not self._is_miro_symbol:
314
- self.modified = False
314
+ self._modified = False
315
315
 
316
316
  self.container._synch_with_gams(gams_to_gamspy=self._is_miro_input)
317
317
 
@@ -319,7 +319,7 @@ class Parameter(gt.Parameter, operable.Operable, Symbol):
319
319
 
320
320
  def _serialize(self) -> dict:
321
321
  info = {
322
- "_domain_forwarding": self.domain_forwarding,
322
+ "_domain_forwarding": self._domain_forwarding,
323
323
  "_is_miro_input": self._is_miro_input,
324
324
  "_is_miro_output": self._is_miro_output,
325
325
  "_is_miro_table": self._is_miro_table,
@@ -490,12 +490,12 @@ class Parameter(gt.Parameter, operable.Operable, Symbol):
490
490
  self._records = records
491
491
 
492
492
  self._requires_state_check = True
493
- self.modified = True
493
+ self._modified = True
494
494
 
495
495
  self.container._requires_state_check = True
496
496
  self.container.modified = True
497
497
 
498
- if self._records is not None and self.domain_forwarding:
498
+ if self._records is not None and self._domain_forwarding:
499
499
  self._domainForwarding()
500
500
 
501
501
  # reset state check flags for all symbols in the container
@@ -562,7 +562,7 @@ class Parameter(gt.Parameter, operable.Operable, Symbol):
562
562
  """
563
563
  representation = self.name
564
564
  if self.domain:
565
- representation += self._get_domain_str(self.domain_forwarding)
565
+ representation += self._get_domain_str(self._domain_forwarding)
566
566
 
567
567
  return representation
568
568