gamspy 1.15.1__tar.gz → 1.16.0__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 (94) hide show
  1. {gamspy-1.15.1 → gamspy-1.16.0}/PKG-INFO +6 -6
  2. {gamspy-1.15.1 → gamspy-1.16.0}/pyproject.toml +6 -6
  3. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/_algebra/condition.py +37 -7
  4. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/_algebra/domain.py +1 -0
  5. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/_algebra/operation.py +7 -1
  6. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/_backend/engine.py +1 -6
  7. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/_cli/install.py +2 -7
  8. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/_symbols/equation.py +25 -14
  9. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/_symbols/implicits/implicit_equation.py +1 -1
  10. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/_symbols/implicits/implicit_parameter.py +7 -11
  11. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/_symbols/implicits/implicit_variable.py +1 -1
  12. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/formulations/ml/gradient_boosting.py +35 -10
  13. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/formulations/ml/random_forest.py +34 -9
  14. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/formulations/ml/regression_tree.py +42 -33
  15. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/math/matrix.py +3 -1
  16. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/utils.py +16 -0
  17. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy.egg-info/PKG-INFO +6 -6
  18. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy.egg-info/requires.txt +4 -4
  19. {gamspy-1.15.1 → gamspy-1.16.0}/tests/test_gamspy.py +1 -1
  20. {gamspy-1.15.1 → gamspy-1.16.0}/LICENSE +0 -0
  21. {gamspy-1.15.1 → gamspy-1.16.0}/README.md +0 -0
  22. {gamspy-1.15.1 → gamspy-1.16.0}/README_PYPI.md +0 -0
  23. {gamspy-1.15.1 → gamspy-1.16.0}/setup.cfg +0 -0
  24. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/__init__.py +0 -0
  25. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/__main__.py +0 -0
  26. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/_algebra/__init__.py +0 -0
  27. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/_algebra/expression.py +0 -0
  28. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/_algebra/number.py +0 -0
  29. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/_algebra/operable.py +0 -0
  30. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/_backend/__init__.py +0 -0
  31. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/_backend/backend.py +0 -0
  32. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/_backend/local.py +0 -0
  33. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/_backend/neos.py +0 -0
  34. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/_cli/__init__.py +0 -0
  35. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/_cli/cli.py +0 -0
  36. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/_cli/gdx.py +0 -0
  37. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/_cli/list.py +0 -0
  38. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/_cli/probe.py +0 -0
  39. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/_cli/retrieve.py +0 -0
  40. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/_cli/run.py +0 -0
  41. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/_cli/show.py +0 -0
  42. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/_cli/uninstall.py +0 -0
  43. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/_cli/util.py +0 -0
  44. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/_config.py +0 -0
  45. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/_container.py +0 -0
  46. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/_convert.py +0 -0
  47. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/_database.py +0 -0
  48. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/_extrinsic.py +0 -0
  49. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/_miro.py +0 -0
  50. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/_model.py +0 -0
  51. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/_model_instance.py +0 -0
  52. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/_options.py +0 -0
  53. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/_serialization.py +0 -0
  54. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/_symbols/__init__.py +0 -0
  55. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/_symbols/alias.py +0 -0
  56. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/_symbols/implicits/__init__.py +0 -0
  57. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/_symbols/implicits/implicit_set.py +0 -0
  58. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/_symbols/implicits/implicit_symbol.py +0 -0
  59. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/_symbols/parameter.py +0 -0
  60. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/_symbols/set.py +0 -0
  61. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/_symbols/symbol.py +0 -0
  62. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/_symbols/universe_alias.py +0 -0
  63. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/_symbols/variable.py +0 -0
  64. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/_types.py +0 -0
  65. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/_validation.py +0 -0
  66. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/_workspace.py +0 -0
  67. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/exceptions.py +0 -0
  68. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/formulations/__init__.py +0 -0
  69. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/formulations/ml/__init__.py +0 -0
  70. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/formulations/ml/decision_tree_struct.py +0 -0
  71. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/formulations/nn/__init__.py +0 -0
  72. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/formulations/nn/avgpool2d.py +0 -0
  73. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/formulations/nn/conv1d.py +0 -0
  74. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/formulations/nn/conv2d.py +0 -0
  75. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/formulations/nn/linear.py +0 -0
  76. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/formulations/nn/maxpool2d.py +0 -0
  77. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/formulations/nn/minpool2d.py +0 -0
  78. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/formulations/nn/mpool2d.py +0 -0
  79. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/formulations/nn/torch_sequential.py +0 -0
  80. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/formulations/piecewise.py +0 -0
  81. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/formulations/shape.py +0 -0
  82. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/formulations/utils.py +0 -0
  83. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/math/__init__.py +0 -0
  84. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/math/activation.py +0 -0
  85. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/math/log_power.py +0 -0
  86. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/math/misc.py +0 -0
  87. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/math/probability.py +0 -0
  88. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/math/trigonometric.py +0 -0
  89. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/py.typed +0 -0
  90. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy/version.py +0 -0
  91. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy.egg-info/SOURCES.txt +0 -0
  92. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy.egg-info/dependency_links.txt +0 -0
  93. {gamspy-1.15.1 → gamspy-1.16.0}/src/gamspy.egg-info/entry_points.txt +0 -0
  94. {gamspy-1.15.1 → gamspy-1.16.0}/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.1
3
+ Version: 1.16.0
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/
@@ -19,11 +19,11 @@ Classifier: License :: OSI Approved :: MIT License
19
19
  Classifier: Programming Language :: Python
20
20
  Classifier: Programming Language :: Python :: 3
21
21
  Classifier: Programming Language :: Python :: 3 :: Only
22
- Classifier: Programming Language :: Python :: 3.9
23
22
  Classifier: Programming Language :: Python :: 3.10
24
23
  Classifier: Programming Language :: Python :: 3.11
25
24
  Classifier: Programming Language :: Python :: 3.12
26
25
  Classifier: Programming Language :: Python :: 3.13
26
+ Classifier: Programming Language :: Python :: 3.14
27
27
  Classifier: Operating System :: POSIX
28
28
  Classifier: Operating System :: Unix
29
29
  Classifier: Operating System :: MacOS
@@ -31,13 +31,12 @@ 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.1
35
- Requires-Dist: gamspy_base==50.4.1
34
+ Requires-Dist: gamsapi[transfer]>=51.1.0
35
+ Requires-Dist: gamspy_base>=51.1.0
36
36
  Requires-Dist: pydantic>=2.0
37
37
  Requires-Dist: certifi>=2022.09.14
38
38
  Requires-Dist: urllib3>=2.0.7
39
- Requires-Dist: typer>=0.15.1
40
- Requires-Dist: click<8.2.0
39
+ Requires-Dist: typer>=0.16.0
41
40
  Provides-Extra: dev
42
41
  Requires-Dist: ruff==0.12.0; extra == "dev"
43
42
  Requires-Dist: pre-commit>=3.5.0; extra == "dev"
@@ -67,6 +66,7 @@ Requires-Dist: nbmake>=1.5.3; extra == "doc"
67
66
  Requires-Dist: openpyxl>=3.1.2; extra == "doc"
68
67
  Requires-Dist: sphinx-tabs>=3.4.7; extra == "doc"
69
68
  Requires-Dist: towncrier>=24.8.0; extra == "doc"
69
+ Requires-Dist: geopandas>=1.1.1; extra == "doc"
70
70
  Provides-Extra: torch
71
71
  Requires-Dist: torch>=2.7.0; extra == "torch"
72
72
  Dynamic: license-file
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "gamspy"
7
- version = "1.15.1"
7
+ version = "1.16.0"
8
8
  authors = [
9
9
  { name = "GAMS Development Corporation", email = "support@gams.com" },
10
10
  ]
@@ -25,24 +25,23 @@ classifiers = [
25
25
  "Programming Language :: Python",
26
26
  "Programming Language :: Python :: 3",
27
27
  "Programming Language :: Python :: 3 :: Only",
28
- "Programming Language :: Python :: 3.9",
29
28
  "Programming Language :: Python :: 3.10",
30
29
  "Programming Language :: Python :: 3.11",
31
30
  "Programming Language :: Python :: 3.12",
32
31
  "Programming Language :: Python :: 3.13",
32
+ "Programming Language :: Python :: 3.14",
33
33
  "Operating System :: POSIX",
34
34
  "Operating System :: Unix",
35
35
  "Operating System :: MacOS",
36
36
  "Operating System :: Microsoft :: Windows",
37
37
  ]
38
38
  dependencies = [
39
- "gamsapi[transfer] == 50.4.1",
40
- "gamspy_base == 50.4.1",
39
+ "gamsapi[transfer] >= 51.1.0",
40
+ "gamspy_base >= 51.1.0",
41
41
  "pydantic >= 2.0",
42
42
  "certifi >= 2022.09.14",
43
43
  "urllib3 >= 2.0.7",
44
- "typer >= 0.15.1",
45
- "click < 8.2.0",
44
+ "typer >= 0.16.0",
46
45
  ]
47
46
 
48
47
  [project.urls]
@@ -86,6 +85,7 @@ doc = [
86
85
  "openpyxl>=3.1.2",
87
86
  "sphinx-tabs>=3.4.7",
88
87
  "towncrier>= 24.8.0",
88
+ "geopandas >= 1.1.1",
89
89
  ]
90
90
 
91
91
  torch = [
@@ -2,12 +2,14 @@ from __future__ import annotations
2
2
 
3
3
  from typing import TYPE_CHECKING
4
4
 
5
+ import gamspy._algebra.domain as domain
5
6
  import gamspy._algebra.expression as expression
6
7
  import gamspy._algebra.operable as operable
7
8
  import gamspy._symbols as syms
8
9
  import gamspy._symbols.implicits as implicits
9
10
  import gamspy.utils as utils
10
11
  from gamspy._symbols.implicits.implicit_symbol import ImplicitSymbol
12
+ from gamspy._symbols.symbol import Symbol
11
13
 
12
14
  if TYPE_CHECKING:
13
15
  import pandas as pd
@@ -103,6 +105,9 @@ class Condition(operable.Operable):
103
105
  if isinstance(self.conditioning_on, ImplicitSymbol):
104
106
  self.conditioning_on.parent._assignment = statement
105
107
  self.conditioning_on.parent._winner = "gams"
108
+ elif isinstance(self.conditioning_on, Symbol):
109
+ self.conditioning_on._assignment = statement
110
+ self.conditioning_on._winner = "gams"
106
111
 
107
112
  if isinstance(self.conditioning_on, implicits.ImplicitEquation):
108
113
  self.conditioning_on.parent._definition = statement
@@ -123,13 +128,38 @@ class Condition(operable.Operable):
123
128
  def records(self) -> pd.DataFrame | None:
124
129
  assert self.container is not None
125
130
  assert self.domain is not None
126
- temp_name = "a" + utils._get_unique_name()
127
- temp_param = syms.Parameter._constructor_bypass(
128
- self.container, temp_name, self.domain
129
- )
130
- temp_param[...] = self
131
- del self.container.data[temp_name]
132
- return temp_param.records
131
+ if isinstance(
132
+ self.conditioning_on,
133
+ (syms.Set, syms.Alias, implicits.ImplicitSet),
134
+ ):
135
+ temp_name = "c" + utils._get_unique_name()
136
+ temp_sym = syms.Set._constructor_bypass(
137
+ self.container,
138
+ temp_name,
139
+ self.domain, # type: ignore
140
+ )
141
+ temp_sym[...] = self
142
+ del self.container.data[temp_name]
143
+ elif isinstance(self.conditioning_on, domain.Domain):
144
+ temp_name = "c" + utils._get_unique_name()
145
+ temp_sym = syms.Set._constructor_bypass(
146
+ self.container,
147
+ temp_name,
148
+ self.domain, # type: ignore
149
+ )
150
+ temp_sym[...].where[self.condition] = True
151
+ del self.container.data[temp_name]
152
+ else:
153
+ temp_name = "c" + utils._get_unique_name()
154
+ temp_sym = syms.Parameter._constructor_bypass(
155
+ self.container,
156
+ temp_name,
157
+ self.domain, # type: ignore
158
+ )
159
+ temp_sym[...] = self
160
+ del self.container.data[temp_name]
161
+
162
+ return temp_sym.records
133
163
 
134
164
  def gamsRepr(self) -> str:
135
165
  condition_str = (
@@ -39,6 +39,7 @@ class Domain:
39
39
  self._sanity_check(sets)
40
40
  self.sets = sets
41
41
  self.container = self._find_container() # type: ignore
42
+ self.domain = sets
42
43
  self.where = condition.Condition(self)
43
44
 
44
45
  def __repr__(self) -> str:
@@ -744,12 +744,18 @@ class Ord(operable.Operable):
744
744
 
745
745
  """
746
746
 
747
- def __init__(self, symbol: Set | Alias):
747
+ def __new__(cls, symbol: Set | Alias):
748
748
  if not isinstance(symbol, (syms.Set, syms.Alias)):
749
749
  raise ValidationError(
750
750
  "Ord operation is only for Set and Alias objects!"
751
751
  )
752
752
 
753
+ if symbol.is_singleton:
754
+ return 1
755
+
756
+ return super().__new__(cls)
757
+
758
+ def __init__(self, symbol: Set | Alias):
753
759
  self._symbol = symbol
754
760
  self.container = symbol.container
755
761
  self.domain: list[Set | Alias] = []
@@ -13,9 +13,6 @@ import urllib.parse
13
13
  import zipfile
14
14
  from typing import TYPE_CHECKING
15
15
 
16
- import certifi
17
- import urllib3
18
-
19
16
  import gamspy._backend.backend as backend
20
17
  import gamspy.utils as utils
21
18
  from gamspy._options import Options
@@ -747,9 +744,7 @@ class EngineClient:
747
744
  self.is_blocking = is_blocking
748
745
  self.tokens: list[str] = []
749
746
 
750
- self._http = urllib3.PoolManager(
751
- cert_reqs="CERT_REQUIRED", ca_certs=certifi.where()
752
- )
747
+ self._http = utils._make_http()
753
748
 
754
749
  self._engine_config = EngineConfiguration(
755
750
  self.host,
@@ -5,7 +5,6 @@ import shutil
5
5
  import sys
6
6
  from typing import Annotated, Iterable, Optional, Union
7
7
 
8
- import certifi
9
8
  import typer
10
9
  from gamspy.exceptions import GamspyException, ValidationError
11
10
  import gamspy.utils as utils
@@ -35,8 +34,6 @@ def license(
35
34
  import json
36
35
  from urllib.parse import urlencode
37
36
 
38
- import urllib3
39
-
40
37
  os.makedirs(utils.DEFAULT_DIR, exist_ok=True)
41
38
 
42
39
  is_alp = not os.path.isfile(license)
@@ -56,10 +53,8 @@ def license(
56
53
 
57
54
  # Make cmex_type check only for GAMS license server.
58
55
  if server == "https://license.gams.com":
59
- http = urllib3.PoolManager(
60
- cert_reqs="CERT_REQUIRED",
61
- ca_certs=certifi.where()
62
- )
56
+ http = utils._make_http()
57
+
63
58
  encoded_args = urlencode({"access_token": alp_id})
64
59
  request = http.request(
65
60
  "GET", f"{server}/license-type?" + encoded_args
@@ -34,9 +34,9 @@ if TYPE_CHECKING:
34
34
  from gamspy._types import EllipsisType
35
35
 
36
36
 
37
- eq_types = ["=e=", "=l=", "=g="]
37
+ EQ_TYPES = ["=e=", "=l=", "=g=", "=n=", "=x=", "=b="]
38
38
 
39
- non_regular_map = {
39
+ IRREGULAR_EQ_MAP = {
40
40
  "nonbinding": "=n=",
41
41
  "external": "=x=",
42
42
  "boolean": "=b=",
@@ -44,10 +44,10 @@ non_regular_map = {
44
44
 
45
45
 
46
46
  class EquationType(Enum):
47
- REGULAR = "REGULAR"
48
- NONBINDING = "NONBINDING"
49
- EXTERNAL = "EXTERNAL"
50
- BOOLEAN = "BOOLEAN"
47
+ REGULAR = "regular"
48
+ NONBINDING = "nonbinding"
49
+ EXTERNAL = "external"
50
+ BOOLEAN = "boolean"
51
51
 
52
52
  @classmethod
53
53
  def values(cls):
@@ -527,18 +527,25 @@ class Equation(gt.Equation, Symbol):
527
527
 
528
528
  def _set_definition(self, domain, rhs):
529
529
  # self[domain] = rhs
530
+ rhs_repr = rhs.gamsRepr()
531
+ if self.type == "nonbinding" and not any(
532
+ eq_type in rhs_repr for eq_type in EQ_TYPES
533
+ ):
534
+ # x - c -> x - c == 0
535
+ rhs = rhs == 0
530
536
 
531
- if not any(eq_type in rhs.gamsRepr() for eq_type in eq_types):
537
+ rhs_repr = rhs.gamsRepr()
538
+ if not any(eq_type in rhs_repr for eq_type in EQ_TYPES):
532
539
  raise ValidationError(
533
540
  "Equation definition must contain at least one equality sign such as ==, <= or >=."
534
541
  )
535
542
 
543
+ if self.type in IRREGULAR_EQ_MAP and "=e=" in rhs_repr:
544
+ rhs.operator = IRREGULAR_EQ_MAP[self.type]
545
+
536
546
  if self.type == "external" and "=e=" not in rhs.gamsRepr():
537
547
  raise ValidationError("External equations must contain ==")
538
548
 
539
- if self.type in non_regular_map:
540
- rhs._replace_operator(non_regular_map[self.type])
541
-
542
549
  statement = expression.Expression(
543
550
  implicits.ImplicitEquation(
544
551
  self,
@@ -1122,10 +1129,13 @@ class Equation(gt.Equation, Symbol):
1122
1129
  >>> i = gp.Set(m, "i", records=['i1','i2'])
1123
1130
  >>> e = gp.Equation(m, "e", domain=[i])
1124
1131
  >>> e.gamsRepr()
1125
- 'e'
1132
+ 'e(i)'
1126
1133
 
1127
1134
  """
1128
- return self.name
1135
+ representation = self.name
1136
+ if self.domain:
1137
+ representation += self._get_domain_str(self._domain_forwarding)
1138
+ return representation
1129
1139
 
1130
1140
  def latexRepr(self) -> str:
1131
1141
  if self._definition is None:
@@ -1255,7 +1265,8 @@ class Equation(gt.Equation, Symbol):
1255
1265
 
1256
1266
  def cast_type(type: str | EquationType) -> str:
1257
1267
  if isinstance(type, str):
1258
- if type.lower() not in (
1268
+ type = type.lower()
1269
+ if type not in (
1259
1270
  "eq",
1260
1271
  "geq",
1261
1272
  "leq",
@@ -1270,7 +1281,7 @@ def cast_type(type: str | EquationType) -> str:
1270
1281
  )
1271
1282
 
1272
1283
  # assign eq by default
1273
- if type.upper() == "REGULAR":
1284
+ if type == "regular":
1274
1285
  type = "eq"
1275
1286
 
1276
1287
  elif isinstance(type, EquationType):
@@ -139,7 +139,7 @@ class ImplicitEquation(ImplicitSymbol):
139
139
  return self.parent.slack
140
140
 
141
141
  @property
142
- def records(self) -> pd.DataFrame | float | None:
142
+ def records(self) -> pd.DataFrame | None:
143
143
  if self.parent.records is None:
144
144
  return None
145
145
 
@@ -157,7 +157,7 @@ class ImplicitParameter(ImplicitSymbol, operable.Operable):
157
157
  return f"ImplicitParameter(parent={self.parent}, name='{self.name}', domain={self.domain}, permutation={self.permutation}), parent_scalar_domains={self.parent_scalar_domains})"
158
158
 
159
159
  @property
160
- def records(self) -> pd.DataFrame | float | None:
160
+ def records(self) -> pd.DataFrame | None:
161
161
  if self.parent.records is None:
162
162
  return None
163
163
 
@@ -179,20 +179,16 @@ class ImplicitParameter(ImplicitSymbol, operable.Operable):
179
179
  for i, d in self._scalar_domains:
180
180
  domain.insert(i, d)
181
181
 
182
+ if domain == []:
183
+ domain = [...]
184
+
182
185
  temp_param[domain] = self[...]
183
186
  del self.container.data[temp_name]
184
187
 
185
188
  recs = temp_param.records
186
- if len(recs) == 1:
187
- return float(recs["value"].squeeze())
188
-
189
189
  return recs
190
190
  elif isinstance(self.parent, (syms.Variable, syms.Equation)):
191
191
  extension = self.name.split(".")[-1]
192
- if self.parent.dimension == 0:
193
- extension = ATTR_MAPPING[extension]
194
- return float(self.parent.records[extension].squeeze())
195
-
196
192
  temp_name = "ip" + utils._get_unique_name()
197
193
  temp_param = syms.Parameter._constructor_bypass(
198
194
  self.container, temp_name, self.parent.domain
@@ -201,6 +197,9 @@ class ImplicitParameter(ImplicitSymbol, operable.Operable):
201
197
  for i, d in self._scalar_domains:
202
198
  domain.insert(i, d)
203
199
 
200
+ if domain == []:
201
+ domain = [...]
202
+
204
203
  temp_param[domain] = self
205
204
  del self.container.data[temp_name]
206
205
 
@@ -213,9 +212,6 @@ class ImplicitParameter(ImplicitSymbol, operable.Operable):
213
212
  columns[columns.index("value")] = extension
214
213
  recs.columns = columns
215
214
 
216
- if len(recs) == 1:
217
- return float(recs[extension].squeeze())
218
-
219
215
  return recs
220
216
 
221
217
  return None
@@ -214,7 +214,7 @@ class ImplicitVariable(ImplicitSymbol, operable.Operable):
214
214
  return permute(self, dims) # type: ignore
215
215
 
216
216
  @property
217
- def records(self) -> pd.DataFrame | float | None:
217
+ def records(self) -> pd.DataFrame | None:
218
218
  if self.parent.records is None:
219
219
  return None
220
220
 
@@ -1,8 +1,9 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import importlib
4
+ import itertools
4
5
  import uuid
5
- from typing import TYPE_CHECKING
6
+ from typing import TYPE_CHECKING, Any
6
7
 
7
8
  import gamspy as gp
8
9
  import gamspy.formulations.utils as utils
@@ -14,6 +15,14 @@ if TYPE_CHECKING:
14
15
  from sklearn.ensemble import GradientBoostingRegressor
15
16
  from sklearn.tree import DecisionTreeRegressor
16
17
 
18
+ from gamspy import (
19
+ Alias,
20
+ Equation,
21
+ Parameter,
22
+ Set,
23
+ Variable,
24
+ )
25
+
17
26
 
18
27
  class GradientBoosting:
19
28
  """
@@ -155,26 +164,42 @@ class GradientBoosting:
155
164
  gb_out_list: list[gp.Variable] = []
156
165
  gb_eqn_list: list[gp.Equation] = []
157
166
 
158
- for regression_tree in self._list_of_trees:
159
- dt_out, dt_eqn, set_of_output_dim = regression_tree(
160
- input, M, is_ensemble=True
161
- )
162
- gb_out_list.append(dt_out)
163
- gb_eqn_list += dt_eqn
167
+ set_records_total: dict[
168
+ Set | Alias | Parameter | Variable | Equation, Any
169
+ ] = {}
170
+
171
+ results = (
172
+ regression_tree._yield_call(input, M)
173
+ for regression_tree in self._list_of_trees
174
+ )
175
+
176
+ zipped_results = zip(*results)
177
+ dt_outs = next(zipped_results)
178
+ gb_out_list.extend(dt_outs)
179
+
180
+ set_records_iter = next(zipped_results)
181
+ set_of_output_dim = None
182
+ for item, set_records_dict in set_records_iter:
183
+ set_records_total.update(set_records_dict)
184
+ set_of_output_dim = item
185
+
186
+ self.container.setRecords(set_records_total)
187
+
188
+ gb_eqn_list = list(itertools.chain.from_iterable(next(zipped_results)))
164
189
 
165
190
  set_of_samples = input.domain[0]
166
191
 
167
192
  out = gp.Variable._constructor_bypass(
168
193
  self.container,
169
194
  name=utils._generate_name("v", self._name_prefix, "real_output"),
170
- domain=[set_of_samples, set_of_output_dim],
195
+ domain=[set_of_samples, set_of_output_dim], # type: ignore
171
196
  )
172
197
 
173
198
  gb_eqn = gp.Equation._constructor_bypass(
174
199
  self.container,
175
200
  name=utils._generate_name("e", self._name_prefix, "gb_eqn"),
176
- domain=[set_of_samples, set_of_output_dim],
177
- description="perdicted out should be equal to the sum of gradient descent out times the learning rate.",
201
+ domain=[set_of_samples, set_of_output_dim], # type: ignore
202
+ description="predicted out should be equal to the sum of gradient descent out times the learning rate.",
178
203
  )
179
204
 
180
205
  self.container._synch_with_gams(gams_to_gamspy=True)
@@ -1,8 +1,9 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import importlib
4
+ import itertools
4
5
  import uuid
5
- from typing import TYPE_CHECKING
6
+ from typing import TYPE_CHECKING, Any
6
7
 
7
8
  import gamspy as gp
8
9
  import gamspy.formulations.utils as utils
@@ -14,6 +15,14 @@ if TYPE_CHECKING:
14
15
  from sklearn.ensemble import RandomForestRegressor
15
16
  from sklearn.tree import DecisionTreeRegressor
16
17
 
18
+ from gamspy import (
19
+ Alias,
20
+ Equation,
21
+ Parameter,
22
+ Set,
23
+ Variable,
24
+ )
25
+
17
26
 
18
27
  class RandomForest:
19
28
  """
@@ -138,25 +147,41 @@ class RandomForest:
138
147
  previous_value = gp.get_option("DOMAIN_VALIDATION")
139
148
  gp.set_options({"DOMAIN_VALIDATION": 0})
140
149
 
141
- for regression_tree in self._list_of_trees:
142
- dt_out, dt_eqn, set_of_output_dim = regression_tree(
143
- input, M, is_ensemble=True
144
- )
145
- rf_out_list.append(dt_out)
146
- rf_eqn_list += dt_eqn
150
+ set_records_total: dict[
151
+ Set | Alias | Parameter | Variable | Equation, Any
152
+ ] = {}
153
+
154
+ results = (
155
+ regression_tree._yield_call(input, M)
156
+ for regression_tree in self._list_of_trees
157
+ )
158
+
159
+ zipped_results = zip(*results)
160
+ dt_outs = next(zipped_results)
161
+ rf_out_list.extend(dt_outs)
162
+
163
+ set_records_iter = next(zipped_results)
164
+ set_of_output_dim = None
165
+ for item, set_records_dict in set_records_iter:
166
+ set_records_total.update(set_records_dict)
167
+ set_of_output_dim = item
168
+
169
+ self.container.setRecords(set_records_total)
170
+
171
+ rf_eqn_list = list(itertools.chain.from_iterable(next(zipped_results)))
147
172
 
148
173
  set_of_samples = input.domain[0]
149
174
 
150
175
  out = gp.Variable._constructor_bypass(
151
176
  self.container,
152
177
  name=utils._generate_name("v", self._name_prefix, "real_output"),
153
- domain=[set_of_samples, set_of_output_dim],
178
+ domain=[set_of_samples, set_of_output_dim], # type: ignore
154
179
  )
155
180
 
156
181
  rf_eqn = gp.Equation._constructor_bypass(
157
182
  self.container,
158
183
  name=utils._generate_name("e", self._name_prefix, "rf_eqn"),
159
- domain=[set_of_samples, set_of_output_dim],
184
+ domain=[set_of_samples, set_of_output_dim], # type: ignore
160
185
  description="predicted out times number of estimators should be equal to the random forest out",
161
186
  )
162
187
 
@@ -181,25 +181,11 @@ class RegressionTree:
181
181
 
182
182
  return node_lb, node_ub
183
183
 
184
- def __call__(
184
+ def _yield_call(
185
185
  self,
186
186
  input: gp.Parameter | gp.Variable,
187
187
  M: float | None = None,
188
- **kwargs,
189
- ) -> tuple[gp.Variable, list[gp.Equation]]:
190
- """
191
- Generate output variable and equations required for embedding the regression tree.
192
-
193
- Parameters
194
- ----------
195
- input : gp.Parameter | gp.Variable
196
- input for the regression tree, must be in shape (sample_size, number_of_features)
197
- M : float
198
- value for the big_M. By default, infer the value using the available bounds for variables.
199
- If the variable is unbounded, then default to 1e10.
200
- """
201
- is_ensemble = kwargs.get("is_ensemble", False)
202
-
188
+ ):
203
189
  leafs = self.children_left < 0
204
190
  leafs = leafs.nonzero()[0]
205
191
  nleafs = len(leafs)
@@ -266,6 +252,8 @@ class RegressionTree:
266
252
  domain=[set_of_samples, set_of_output_dim],
267
253
  )
268
254
 
255
+ yield out
256
+
269
257
  feat_vars = gp.Variable._constructor_bypass(
270
258
  self.container,
271
259
  name=utils._generate_name("v", self._name_prefix, "feature"),
@@ -426,19 +414,19 @@ class RegressionTree:
426
414
  lb_output._definition = definition
427
415
  self.container._add_statement(definition)
428
416
 
429
- self.container.setRecords(
430
- {
431
- out_link: [
432
- (int(i), int(j), v)
433
- for i, j, v in np.stack(
434
- (idx, jdx, mapped_values), axis=-1
435
- ).reshape(-1, 3)
436
- ],
437
- feat_par: np.stack([node_lb, node_ub], axis=-1)[:, leafs, :],
438
- max_out: np.max(self.value[leafs, :], axis=0),
439
- min_out: np.min(self.value[leafs, :], axis=0),
440
- }
441
- )
417
+ set_records_dict = {
418
+ out_link: [
419
+ (int(i), int(j), v)
420
+ for i, j, v in np.stack(
421
+ (idx, jdx, mapped_values), axis=-1
422
+ ).reshape(-1, 3)
423
+ ],
424
+ feat_par: np.stack([node_lb, node_ub], axis=-1)[:, leafs, :],
425
+ max_out: np.max(self.value[leafs, :], axis=0),
426
+ min_out: np.min(self.value[leafs, :], axis=0),
427
+ }
428
+
429
+ yield [set_of_output_dim, set_records_dict]
442
430
 
443
431
  ### This generates the set of possible paths given the input data
444
432
  mask = (feat_vars.up[...] >= feat_par[..., lb]) & (
@@ -545,9 +533,30 @@ class RegressionTree:
545
533
  ge_cons,
546
534
  le_cons,
547
535
  ]
536
+ yield eqns
548
537
 
549
- if is_ensemble:
550
- return out, eqns, set_of_output_dim # type: ignore
538
+ def __call__(
539
+ self,
540
+ input: gp.Parameter | gp.Variable,
541
+ M: float | None = None,
542
+ ) -> tuple[gp.Variable, list[gp.Equation]]:
543
+ """
544
+ Generate output variable and equations required for embedding the regression tree.
551
545
 
552
- else:
553
- return out, eqns
546
+ Parameters
547
+ ----------
548
+ input : gp.Parameter | gp.Variable
549
+ input for the regression tree, must be in shape (sample_size, number_of_features)
550
+ M : float
551
+ value for the big_M. By default, infer the value using the available bounds for variables.
552
+ If the variable is unbounded, then default to 1e10.
553
+ """
554
+
555
+ generator = self._yield_call(input, M)
556
+
557
+ output = next(generator)
558
+ _, set_values = next(generator)
559
+ input.container.setRecords(set_values)
560
+ eqs = next(generator)
561
+
562
+ return output, eqs
@@ -223,7 +223,9 @@ def _generate_dims(
223
223
  expected_name = f"DenseDim{x}_1"
224
224
  find_x = m.data.get(expected_name, None)
225
225
  if find_x is None:
226
- find_x = m.addSet(name=expected_name, records=range(x))
226
+ find_x = m.addSet(
227
+ name=expected_name, records=range(x), is_singleton=(x == 1)
228
+ )
227
229
 
228
230
  while find_x in sets_so_far:
229
231
  find_x = next_alias(find_x)
@@ -7,7 +7,9 @@ import platform
7
7
  import uuid
8
8
  from typing import TYPE_CHECKING
9
9
 
10
+ import certifi
10
11
  import gams.transfer as gt
12
+ import urllib3
11
13
  from gams.core import gdx
12
14
 
13
15
  import gamspy._model as model
@@ -813,3 +815,17 @@ def _parse_generated_variables(model: Model, listing_file: str) -> None:
813
815
  variable._column_listing = listings
814
816
 
815
817
  return None
818
+
819
+
820
+ def _make_http() -> urllib3.PoolManager:
821
+ proxy_url = os.getenv("HTTPS_PROXY") or os.getenv("HTTP_PROXY")
822
+ if proxy_url:
823
+ http = urllib3.ProxyManager(
824
+ proxy_url, cert_reqs="CERT_REQUIRED", ca_certs=certifi.where()
825
+ )
826
+ else:
827
+ http = urllib3.PoolManager(
828
+ cert_reqs="CERT_REQUIRED", ca_certs=certifi.where()
829
+ )
830
+
831
+ return http
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gamspy
3
- Version: 1.15.1
3
+ Version: 1.16.0
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/
@@ -19,11 +19,11 @@ Classifier: License :: OSI Approved :: MIT License
19
19
  Classifier: Programming Language :: Python
20
20
  Classifier: Programming Language :: Python :: 3
21
21
  Classifier: Programming Language :: Python :: 3 :: Only
22
- Classifier: Programming Language :: Python :: 3.9
23
22
  Classifier: Programming Language :: Python :: 3.10
24
23
  Classifier: Programming Language :: Python :: 3.11
25
24
  Classifier: Programming Language :: Python :: 3.12
26
25
  Classifier: Programming Language :: Python :: 3.13
26
+ Classifier: Programming Language :: Python :: 3.14
27
27
  Classifier: Operating System :: POSIX
28
28
  Classifier: Operating System :: Unix
29
29
  Classifier: Operating System :: MacOS
@@ -31,13 +31,12 @@ 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.1
35
- Requires-Dist: gamspy_base==50.4.1
34
+ Requires-Dist: gamsapi[transfer]>=51.1.0
35
+ Requires-Dist: gamspy_base>=51.1.0
36
36
  Requires-Dist: pydantic>=2.0
37
37
  Requires-Dist: certifi>=2022.09.14
38
38
  Requires-Dist: urllib3>=2.0.7
39
- Requires-Dist: typer>=0.15.1
40
- Requires-Dist: click<8.2.0
39
+ Requires-Dist: typer>=0.16.0
41
40
  Provides-Extra: dev
42
41
  Requires-Dist: ruff==0.12.0; extra == "dev"
43
42
  Requires-Dist: pre-commit>=3.5.0; extra == "dev"
@@ -67,6 +66,7 @@ Requires-Dist: nbmake>=1.5.3; extra == "doc"
67
66
  Requires-Dist: openpyxl>=3.1.2; extra == "doc"
68
67
  Requires-Dist: sphinx-tabs>=3.4.7; extra == "doc"
69
68
  Requires-Dist: towncrier>=24.8.0; extra == "doc"
69
+ Requires-Dist: geopandas>=1.1.1; extra == "doc"
70
70
  Provides-Extra: torch
71
71
  Requires-Dist: torch>=2.7.0; extra == "torch"
72
72
  Dynamic: license-file
@@ -1,10 +1,9 @@
1
- gamsapi[transfer]==50.4.1
2
- gamspy_base==50.4.1
1
+ gamsapi[transfer]>=51.1.0
2
+ gamspy_base>=51.1.0
3
3
  pydantic>=2.0
4
4
  certifi>=2022.09.14
5
5
  urllib3>=2.0.7
6
- typer>=0.15.1
7
- click<8.2.0
6
+ typer>=0.16.0
8
7
 
9
8
  [dev]
10
9
  ruff==0.12.0
@@ -28,6 +27,7 @@ nbmake>=1.5.3
28
27
  openpyxl>=3.1.2
29
28
  sphinx-tabs>=3.4.7
30
29
  towncrier>=24.8.0
30
+ geopandas>=1.1.1
31
31
 
32
32
  [test]
33
33
  coverage[toml]>=7.2.7
@@ -17,7 +17,7 @@ from gamspy.exceptions import GamspyException, ValidationError
17
17
 
18
18
  @pytest.mark.unit
19
19
  def test_version():
20
- assert gp.__version__ == "1.15.1"
20
+ assert gp.__version__ == "1.16.0"
21
21
 
22
22
 
23
23
  @pytest.mark.unit
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes