gamspy 1.15.0__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.
- {gamspy-1.15.0 → gamspy-1.16.0}/PKG-INFO +6 -6
- {gamspy-1.15.0 → gamspy-1.16.0}/pyproject.toml +6 -6
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy/_algebra/condition.py +37 -7
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy/_algebra/domain.py +1 -0
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy/_algebra/expression.py +3 -0
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy/_algebra/operation.py +7 -1
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy/_backend/engine.py +1 -6
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy/_cli/install.py +3 -8
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy/_cli/retrieve.py +13 -6
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy/_config.py +5 -0
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy/_container.py +1 -1
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy/_convert.py +1 -1
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy/_miro.py +5 -1
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy/_symbols/alias.py +2 -2
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy/_symbols/equation.py +65 -22
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy/_symbols/implicits/implicit_equation.py +19 -5
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy/_symbols/implicits/implicit_parameter.py +45 -53
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy/_symbols/implicits/implicit_set.py +16 -5
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy/_symbols/implicits/implicit_variable.py +18 -5
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy/_symbols/parameter.py +9 -9
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy/_symbols/set.py +8 -8
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy/_symbols/symbol.py +1 -1
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy/_symbols/variable.py +8 -8
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy/_validation.py +44 -8
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy/formulations/__init__.py +8 -1
- gamspy-1.16.0/src/gamspy/formulations/ml/__init__.py +11 -0
- gamspy-1.16.0/src/gamspy/formulations/ml/gradient_boosting.py +211 -0
- gamspy-1.16.0/src/gamspy/formulations/ml/random_forest.py +193 -0
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy/formulations/ml/regression_tree.py +245 -135
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy/formulations/nn/avgpool2d.py +1 -1
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy/formulations/nn/conv1d.py +17 -4
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy/formulations/nn/conv2d.py +18 -4
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy/formulations/nn/linear.py +17 -4
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy/formulations/nn/mpool2d.py +1 -1
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy/formulations/piecewise.py +4 -6
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy/formulations/shape.py +1 -1
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy/math/activation.py +14 -0
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy/math/matrix.py +3 -1
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy/utils.py +16 -0
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy.egg-info/PKG-INFO +6 -6
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy.egg-info/SOURCES.txt +3 -1
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy.egg-info/requires.txt +4 -4
- {gamspy-1.15.0 → gamspy-1.16.0}/tests/test_gamspy.py +1 -1
- gamspy-1.15.0/src/gamspy/formulations/ml/__init__.py +0 -4
- {gamspy-1.15.0 → gamspy-1.16.0}/LICENSE +0 -0
- {gamspy-1.15.0 → gamspy-1.16.0}/README.md +0 -0
- {gamspy-1.15.0 → gamspy-1.16.0}/README_PYPI.md +0 -0
- {gamspy-1.15.0 → gamspy-1.16.0}/setup.cfg +0 -0
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy/__init__.py +0 -0
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy/__main__.py +0 -0
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy/_algebra/__init__.py +0 -0
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy/_algebra/number.py +0 -0
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy/_algebra/operable.py +0 -0
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy/_backend/__init__.py +0 -0
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy/_backend/backend.py +0 -0
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy/_backend/local.py +0 -0
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy/_backend/neos.py +0 -0
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy/_cli/__init__.py +0 -0
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy/_cli/cli.py +0 -0
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy/_cli/gdx.py +0 -0
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy/_cli/list.py +0 -0
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy/_cli/probe.py +0 -0
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy/_cli/run.py +0 -0
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy/_cli/show.py +0 -0
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy/_cli/uninstall.py +0 -0
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy/_cli/util.py +0 -0
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy/_database.py +0 -0
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy/_extrinsic.py +0 -0
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy/_model.py +0 -0
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy/_model_instance.py +0 -0
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy/_options.py +0 -0
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy/_serialization.py +0 -0
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy/_symbols/__init__.py +0 -0
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy/_symbols/implicits/__init__.py +0 -0
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy/_symbols/implicits/implicit_symbol.py +0 -0
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy/_symbols/universe_alias.py +0 -0
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy/_types.py +0 -0
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy/_workspace.py +0 -0
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy/exceptions.py +0 -0
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy/formulations/ml/decision_tree_struct.py +0 -0
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy/formulations/nn/__init__.py +0 -0
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy/formulations/nn/maxpool2d.py +0 -0
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy/formulations/nn/minpool2d.py +0 -0
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy/formulations/nn/torch_sequential.py +0 -0
- {gamspy-1.15.0/src/gamspy/formulations/nn → gamspy-1.16.0/src/gamspy/formulations}/utils.py +0 -0
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy/math/__init__.py +0 -0
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy/math/log_power.py +0 -0
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy/math/misc.py +0 -0
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy/math/probability.py +0 -0
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy/math/trigonometric.py +0 -0
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy/py.typed +0 -0
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy/version.py +0 -0
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy.egg-info/dependency_links.txt +0 -0
- {gamspy-1.15.0 → gamspy-1.16.0}/src/gamspy.egg-info/entry_points.txt +0 -0
- {gamspy-1.15.0 → 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.
|
|
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]
|
|
35
|
-
Requires-Dist: gamspy_base
|
|
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.
|
|
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.
|
|
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]
|
|
40
|
-
"gamspy_base
|
|
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.
|
|
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
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
)
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
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 = (
|
|
@@ -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
|
|
@@ -744,12 +744,18 @@ class Ord(operable.Operable):
|
|
|
744
744
|
|
|
745
745
|
"""
|
|
746
746
|
|
|
747
|
-
def
|
|
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 =
|
|
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 =
|
|
60
|
-
|
|
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
|
|
@@ -261,7 +256,7 @@ def solver(
|
|
|
261
256
|
_ = subprocess.run(command, check=True, encoding="utf-8", stderr=subprocess.PIPE)
|
|
262
257
|
except subprocess.CalledProcessError as e:
|
|
263
258
|
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
|
|
259
|
+
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
260
|
) from e
|
|
266
261
|
else:
|
|
267
262
|
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
|
],
|
|
@@ -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
|
|
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.
|
|
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.
|
|
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
|
|
@@ -33,9 +34,9 @@ if TYPE_CHECKING:
|
|
|
33
34
|
from gamspy._types import EllipsisType
|
|
34
35
|
|
|
35
36
|
|
|
36
|
-
|
|
37
|
+
EQ_TYPES = ["=e=", "=l=", "=g=", "=n=", "=x=", "=b="]
|
|
37
38
|
|
|
38
|
-
|
|
39
|
+
IRREGULAR_EQ_MAP = {
|
|
39
40
|
"nonbinding": "=n=",
|
|
40
41
|
"external": "=x=",
|
|
41
42
|
"boolean": "=b=",
|
|
@@ -43,10 +44,10 @@ non_regular_map = {
|
|
|
43
44
|
|
|
44
45
|
|
|
45
46
|
class EquationType(Enum):
|
|
46
|
-
REGULAR = "
|
|
47
|
-
NONBINDING = "
|
|
48
|
-
EXTERNAL = "
|
|
49
|
-
BOOLEAN = "
|
|
47
|
+
REGULAR = "regular"
|
|
48
|
+
NONBINDING = "nonbinding"
|
|
49
|
+
EXTERNAL = "external"
|
|
50
|
+
BOOLEAN = "boolean"
|
|
50
51
|
|
|
51
52
|
@classmethod
|
|
52
53
|
def values(cls):
|
|
@@ -273,7 +274,7 @@ class Equation(gt.Equation, Symbol):
|
|
|
273
274
|
" domains are equal"
|
|
274
275
|
)
|
|
275
276
|
|
|
276
|
-
if self.
|
|
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.
|
|
291
|
-
self.
|
|
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.
|
|
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.
|
|
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,
|
|
@@ -526,18 +527,25 @@ class Equation(gt.Equation, Symbol):
|
|
|
526
527
|
|
|
527
528
|
def _set_definition(self, domain, rhs):
|
|
528
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
|
|
529
536
|
|
|
530
|
-
|
|
537
|
+
rhs_repr = rhs.gamsRepr()
|
|
538
|
+
if not any(eq_type in rhs_repr for eq_type in EQ_TYPES):
|
|
531
539
|
raise ValidationError(
|
|
532
540
|
"Equation definition must contain at least one equality sign such as ==, <= or >=."
|
|
533
541
|
)
|
|
534
542
|
|
|
543
|
+
if self.type in IRREGULAR_EQ_MAP and "=e=" in rhs_repr:
|
|
544
|
+
rhs.operator = IRREGULAR_EQ_MAP[self.type]
|
|
545
|
+
|
|
535
546
|
if self.type == "external" and "=e=" not in rhs.gamsRepr():
|
|
536
547
|
raise ValidationError("External equations must contain ==")
|
|
537
548
|
|
|
538
|
-
if self.type in non_regular_map:
|
|
539
|
-
rhs._replace_operator(non_regular_map[self.type])
|
|
540
|
-
|
|
541
549
|
statement = expression.Expression(
|
|
542
550
|
implicits.ImplicitEquation(
|
|
543
551
|
self,
|
|
@@ -554,6 +562,37 @@ class Equation(gt.Equation, Symbol):
|
|
|
554
562
|
self.container._add_statement(statement)
|
|
555
563
|
self._definition = statement
|
|
556
564
|
|
|
565
|
+
def _check_ambiguity(self) -> None:
|
|
566
|
+
"""Ambiguity check for MCP, EMP, MPEC models. See #610"""
|
|
567
|
+
# Looks for =e=, =l= and =g= in an equation definition
|
|
568
|
+
# with a stack based inorder traversal algorithm (O(N)).
|
|
569
|
+
stack = []
|
|
570
|
+
|
|
571
|
+
assert self._definition is not None
|
|
572
|
+
node = self._definition.right
|
|
573
|
+
while True:
|
|
574
|
+
if node is not None:
|
|
575
|
+
stack.append(node)
|
|
576
|
+
node = getattr(node, "left", None) # type: ignore
|
|
577
|
+
elif stack:
|
|
578
|
+
node = stack.pop()
|
|
579
|
+
if (
|
|
580
|
+
isinstance(node, expression.Expression)
|
|
581
|
+
and node.operator in {"=e=", "=l=", "=g=", "=x=", "=n="}
|
|
582
|
+
and not isinstance(node.right, operable.Operable)
|
|
583
|
+
):
|
|
584
|
+
raise ValidationError(
|
|
585
|
+
f"Definition of `{self.name}` is ambigiuous. Please "
|
|
586
|
+
"use gp.Number for numeric values or disable ambiguity "
|
|
587
|
+
"check via gp.set_options({'ALLOW_AMBIGUOUS_EQUATIONS': 'no'}). "
|
|
588
|
+
"Using numeric values in equations without gp.Number can result in "
|
|
589
|
+
f"different order than expected. Print `{self.name}.getDefinition()` to "
|
|
590
|
+
"make sure that the equation definition is as expected."
|
|
591
|
+
)
|
|
592
|
+
node = getattr(node, "right", None)
|
|
593
|
+
else:
|
|
594
|
+
break # pragma: no cover
|
|
595
|
+
|
|
557
596
|
@property
|
|
558
597
|
def l(self): # noqa: E741, E743
|
|
559
598
|
"""
|
|
@@ -1000,12 +1039,12 @@ class Equation(gt.Equation, Symbol):
|
|
|
1000
1039
|
self._records = records
|
|
1001
1040
|
|
|
1002
1041
|
self._requires_state_check = True
|
|
1003
|
-
self.
|
|
1042
|
+
self._modified = True
|
|
1004
1043
|
|
|
1005
1044
|
self.container._requires_state_check = True
|
|
1006
1045
|
self.container.modified = True
|
|
1007
1046
|
|
|
1008
|
-
if self._records is not None and self.
|
|
1047
|
+
if self._records is not None and self._domain_forwarding:
|
|
1009
1048
|
self._domainForwarding()
|
|
1010
1049
|
|
|
1011
1050
|
# reset state check flags for all symbols in the container
|
|
@@ -1090,10 +1129,13 @@ class Equation(gt.Equation, Symbol):
|
|
|
1090
1129
|
>>> i = gp.Set(m, "i", records=['i1','i2'])
|
|
1091
1130
|
>>> e = gp.Equation(m, "e", domain=[i])
|
|
1092
1131
|
>>> e.gamsRepr()
|
|
1093
|
-
'e'
|
|
1132
|
+
'e(i)'
|
|
1094
1133
|
|
|
1095
1134
|
"""
|
|
1096
|
-
|
|
1135
|
+
representation = self.name
|
|
1136
|
+
if self.domain:
|
|
1137
|
+
representation += self._get_domain_str(self._domain_forwarding)
|
|
1138
|
+
return representation
|
|
1097
1139
|
|
|
1098
1140
|
def latexRepr(self) -> str:
|
|
1099
1141
|
if self._definition is None:
|
|
@@ -1159,7 +1201,7 @@ class Equation(gt.Equation, Symbol):
|
|
|
1159
1201
|
output = f"Equation {self.name}"
|
|
1160
1202
|
|
|
1161
1203
|
if self.domain:
|
|
1162
|
-
output += self._get_domain_str(self.
|
|
1204
|
+
output += self._get_domain_str(self._domain_forwarding)
|
|
1163
1205
|
|
|
1164
1206
|
if self.description:
|
|
1165
1207
|
output += ' "' + self.description + '"'
|
|
@@ -1223,7 +1265,8 @@ class Equation(gt.Equation, Symbol):
|
|
|
1223
1265
|
|
|
1224
1266
|
def cast_type(type: str | EquationType) -> str:
|
|
1225
1267
|
if isinstance(type, str):
|
|
1226
|
-
|
|
1268
|
+
type = type.lower()
|
|
1269
|
+
if type not in (
|
|
1227
1270
|
"eq",
|
|
1228
1271
|
"geq",
|
|
1229
1272
|
"leq",
|
|
@@ -1238,7 +1281,7 @@ def cast_type(type: str | EquationType) -> str:
|
|
|
1238
1281
|
)
|
|
1239
1282
|
|
|
1240
1283
|
# assign eq by default
|
|
1241
|
-
if type
|
|
1284
|
+
if type == "regular":
|
|
1242
1285
|
type = "eq"
|
|
1243
1286
|
|
|
1244
1287
|
elif isinstance(type, EquationType):
|
|
@@ -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:
|
|
@@ -137,14 +139,26 @@ class ImplicitEquation(ImplicitSymbol):
|
|
|
137
139
|
return self.parent.slack
|
|
138
140
|
|
|
139
141
|
@property
|
|
140
|
-
def records(self) -> pd.DataFrame |
|
|
142
|
+
def records(self) -> pd.DataFrame | None:
|
|
141
143
|
if self.parent.records is None:
|
|
142
144
|
return None
|
|
143
145
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
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
|
|