gamspy 1.18.4__py3-none-any.whl → 1.19.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- gamspy/_algebra/condition.py +37 -0
- gamspy/_algebra/operation.py +140 -37
- gamspy/_backend/backend.py +3 -1
- gamspy/_backend/engine.py +10 -10
- gamspy/_backend/local.py +8 -8
- gamspy/_backend/neos.py +10 -10
- gamspy/_cli/install.py +0 -2
- gamspy/_container.py +94 -15
- gamspy/_convert.py +2 -2
- gamspy/_miro.py +6 -2
- gamspy/_model.py +134 -13
- gamspy/_model_instance.py +6 -6
- gamspy/_options.py +7 -5
- gamspy/_symbols/symbol.py +9 -0
- gamspy/_workspace.py +1 -1
- gamspy/formulations/piecewise.py +2 -2
- gamspy/math/activation.py +16 -0
- gamspy/math/misc.py +84 -0
- {gamspy-1.18.4.dist-info → gamspy-1.19.1.dist-info}/METADATA +1 -32
- {gamspy-1.18.4.dist-info → gamspy-1.19.1.dist-info}/RECORD +24 -24
- {gamspy-1.18.4.dist-info → gamspy-1.19.1.dist-info}/WHEEL +1 -1
- {gamspy-1.18.4.dist-info → gamspy-1.19.1.dist-info}/licenses/LICENSE +3 -3
- {gamspy-1.18.4.dist-info → gamspy-1.19.1.dist-info}/entry_points.txt +0 -0
- {gamspy-1.18.4.dist-info → gamspy-1.19.1.dist-info}/top_level.txt +0 -0
gamspy/_container.py
CHANGED
|
@@ -585,7 +585,47 @@ class Container(gt.Container):
|
|
|
585
585
|
|
|
586
586
|
return gams_string
|
|
587
587
|
|
|
588
|
-
def
|
|
588
|
+
def _filter_load_symbols(
|
|
589
|
+
self,
|
|
590
|
+
symbol_names: dict[str, str] | list[str],
|
|
591
|
+
create_if_not_declared: bool = False,
|
|
592
|
+
) -> dict[str, str] | list[str]:
|
|
593
|
+
if isinstance(symbol_names, list):
|
|
594
|
+
names = []
|
|
595
|
+
for name in symbol_names:
|
|
596
|
+
if name in self.data:
|
|
597
|
+
symbol = self[name]
|
|
598
|
+
if not isinstance(symbol, gt.Alias) and symbol.synchronize:
|
|
599
|
+
names.append(name)
|
|
600
|
+
else:
|
|
601
|
+
if create_if_not_declared:
|
|
602
|
+
names.append(name)
|
|
603
|
+
else:
|
|
604
|
+
raise ValidationError(
|
|
605
|
+
f"Cannot load records of `{name}` because it does not exist in the container."
|
|
606
|
+
)
|
|
607
|
+
|
|
608
|
+
return names
|
|
609
|
+
|
|
610
|
+
mapping = {}
|
|
611
|
+
for gdx_name, gamspy_name in symbol_names.items():
|
|
612
|
+
if gamspy_name in self.data:
|
|
613
|
+
if self[gamspy_name].synchronize:
|
|
614
|
+
mapping[gdx_name] = gamspy_name
|
|
615
|
+
else:
|
|
616
|
+
raise ValidationError(
|
|
617
|
+
f"Invalid renaming. `{gamspy_name}` does not exist in the container."
|
|
618
|
+
)
|
|
619
|
+
|
|
620
|
+
return mapping
|
|
621
|
+
|
|
622
|
+
def _load_records_from_gdx(
|
|
623
|
+
self,
|
|
624
|
+
load_from: str,
|
|
625
|
+
names: Iterable[str],
|
|
626
|
+
*,
|
|
627
|
+
create_if_not_declared: bool = False,
|
|
628
|
+
) -> None:
|
|
589
629
|
self._temp_container.read(load_from, names)
|
|
590
630
|
original_state = self._options.miro_protect
|
|
591
631
|
self._options.miro_protect = False
|
|
@@ -596,7 +636,12 @@ class Container(gt.Container):
|
|
|
596
636
|
self[name].records = updated_records
|
|
597
637
|
self[name].domain_labels = self[name].domain_names
|
|
598
638
|
else:
|
|
599
|
-
|
|
639
|
+
if create_if_not_declared:
|
|
640
|
+
self._read(load_from, [name])
|
|
641
|
+
else:
|
|
642
|
+
raise ValidationError(
|
|
643
|
+
f"Cannot load records of `{name}` because it does not exist in the container."
|
|
644
|
+
)
|
|
600
645
|
|
|
601
646
|
self._options.miro_protect = original_state
|
|
602
647
|
self._temp_container.data = {}
|
|
@@ -607,14 +652,9 @@ class Container(gt.Container):
|
|
|
607
652
|
self._options.miro_protect = False
|
|
608
653
|
|
|
609
654
|
for gdx_name, gamspy_name in names.items():
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
self[gamspy_name].domain_labels = self[gamspy_name].domain_names
|
|
614
|
-
else:
|
|
615
|
-
raise ValidationError(
|
|
616
|
-
f"Invalid renaming. `{gamspy_name}` does not exist in the container."
|
|
617
|
-
)
|
|
655
|
+
updated_records = self._temp_container[gdx_name].records
|
|
656
|
+
self[gamspy_name].records = updated_records
|
|
657
|
+
self[gamspy_name].domain_labels = self[gamspy_name].domain_names
|
|
618
658
|
|
|
619
659
|
self._options.miro_protect = original_state
|
|
620
660
|
self._temp_container.data = {}
|
|
@@ -901,7 +941,7 @@ class Container(gt.Container):
|
|
|
901
941
|
"""
|
|
902
942
|
if self._debugging_level != "keep":
|
|
903
943
|
raise ValidationError(
|
|
904
|
-
"`
|
|
944
|
+
"`debugging_level` argument of the container must be set to 'keep' to use this function."
|
|
905
945
|
)
|
|
906
946
|
|
|
907
947
|
gams_string = self._gams_string
|
|
@@ -916,7 +956,7 @@ class Container(gt.Container):
|
|
|
916
956
|
|
|
917
957
|
def loadRecordsFromGdx(
|
|
918
958
|
self,
|
|
919
|
-
load_from: str,
|
|
959
|
+
load_from: str | Path,
|
|
920
960
|
symbol_names: Iterable[str] | dict[str, str] | None = None,
|
|
921
961
|
) -> None:
|
|
922
962
|
"""
|
|
@@ -940,24 +980,63 @@ class Container(gt.Container):
|
|
|
940
980
|
>>> i = Set(m, "i", records=["i1", "i2"])
|
|
941
981
|
>>> m.write("test.gdx")
|
|
942
982
|
>>> m2 = Container()
|
|
983
|
+
>>> i = Set(m2, "i")
|
|
943
984
|
>>> m2.loadRecordsFromGdx("test.gdx")
|
|
944
985
|
>>> print(i.records.equals(m2["i"].records))
|
|
945
986
|
True
|
|
946
987
|
|
|
947
988
|
"""
|
|
989
|
+
if isinstance(load_from, Path):
|
|
990
|
+
load_from = str(load_from.resolve())
|
|
991
|
+
|
|
992
|
+
create_if_not_declared = symbol_names is None
|
|
948
993
|
if symbol_names is None:
|
|
949
994
|
# If no symbol names are given, all records in the gdx should be loaded
|
|
950
995
|
symbol_names = utils._get_symbol_names_from_gdx(
|
|
951
996
|
self.system_directory, load_from
|
|
952
997
|
)
|
|
998
|
+
symbol_names = self._filter_load_symbols(
|
|
999
|
+
symbol_names, # type: ignore
|
|
1000
|
+
create_if_not_declared,
|
|
1001
|
+
)
|
|
1002
|
+
self._load_records_from_gdx(
|
|
1003
|
+
load_from, symbol_names, create_if_not_declared=create_if_not_declared
|
|
1004
|
+
)
|
|
1005
|
+
self._synch_with_gams()
|
|
1006
|
+
return
|
|
953
1007
|
|
|
954
|
-
if isinstance(symbol_names,
|
|
955
|
-
self.
|
|
1008
|
+
if isinstance(symbol_names, list):
|
|
1009
|
+
symbol_names = self._filter_load_symbols(
|
|
1010
|
+
symbol_names, create_if_not_declared
|
|
1011
|
+
)
|
|
1012
|
+
self._add_statement(f"$gdxIn {load_from}")
|
|
1013
|
+
symbols_str = " ".join(symbol_names)
|
|
1014
|
+
self._add_statement(f"$loadDC {symbols_str}")
|
|
1015
|
+
self._add_statement("$gdxIn")
|
|
1016
|
+
elif isinstance(symbol_names, dict):
|
|
1017
|
+
symbol_names = self._filter_load_symbols(
|
|
1018
|
+
symbol_names, create_if_not_declared
|
|
1019
|
+
)
|
|
1020
|
+
self._add_statement(f"$gdxIn {load_from}")
|
|
1021
|
+
symbols_str = " ".join(
|
|
1022
|
+
[f"{value}={key}" for key, value in symbol_names.items()] # type: ignore
|
|
1023
|
+
)
|
|
1024
|
+
self._add_statement(f"$loadDC {symbols_str}")
|
|
1025
|
+
self._add_statement("$gdxIn")
|
|
956
1026
|
else:
|
|
957
|
-
|
|
1027
|
+
raise TypeError("`symbol_names` must be either a list or a dictionary.")
|
|
958
1028
|
|
|
1029
|
+
self._options._debug_options["gdx"] = self._gdx_out
|
|
1030
|
+
self._options._debug_options["gdxSymbols"] = "newOrChanged"
|
|
959
1031
|
self._synch_with_gams()
|
|
960
1032
|
|
|
1033
|
+
if isinstance(symbol_names, dict):
|
|
1034
|
+
self._load_records_with_rename(load_from, symbol_names)
|
|
1035
|
+
else:
|
|
1036
|
+
self._load_records_from_gdx(
|
|
1037
|
+
load_from, symbol_names, create_if_not_declared=create_if_not_declared
|
|
1038
|
+
)
|
|
1039
|
+
|
|
961
1040
|
def addGamsCode(self, gams_code: str) -> None:
|
|
962
1041
|
"""
|
|
963
1042
|
Adds an arbitrary GAMS code to the generate .gms file.
|
gamspy/_convert.py
CHANGED
|
@@ -338,8 +338,8 @@ def get_convert_solver_options(
|
|
|
338
338
|
solver_options[name] = str((path / value).resolve())
|
|
339
339
|
|
|
340
340
|
if options is not None:
|
|
341
|
-
|
|
342
|
-
for key, value in
|
|
341
|
+
convert_options = options.model_dump(exclude_none=True)
|
|
342
|
+
for key, value in convert_options.items():
|
|
343
343
|
name = OPTION_RENAME_MAP.get(key, key)
|
|
344
344
|
solver_options[name] = int(value) if isinstance(value, bool) else value
|
|
345
345
|
|
gamspy/_miro.py
CHANGED
|
@@ -41,7 +41,9 @@ def load_miro_symbol_records(container: Container):
|
|
|
41
41
|
for name in container._miro_input_symbols
|
|
42
42
|
if not container[name]._already_loaded
|
|
43
43
|
]
|
|
44
|
-
container._load_records_from_gdx(
|
|
44
|
+
container._load_records_from_gdx(
|
|
45
|
+
MIRO_GDX_IN, names, create_if_not_declared=True
|
|
46
|
+
)
|
|
45
47
|
for name in names:
|
|
46
48
|
symbol = container[name]
|
|
47
49
|
symbol._already_loaded = True
|
|
@@ -54,7 +56,9 @@ def load_miro_symbol_records(container: Container):
|
|
|
54
56
|
|
|
55
57
|
# Load records of miro output symbols
|
|
56
58
|
if MIRO_GDX_OUT and container._miro_output_symbols:
|
|
57
|
-
container._load_records_from_gdx(
|
|
59
|
+
container._load_records_from_gdx(
|
|
60
|
+
MIRO_GDX_OUT, container._miro_output_symbols, create_if_not_declared=True
|
|
61
|
+
)
|
|
58
62
|
|
|
59
63
|
for name in container._miro_input_symbols + container._miro_output_symbols:
|
|
60
64
|
container[name].modified = False
|
gamspy/_model.py
CHANGED
|
@@ -368,6 +368,7 @@ class Model:
|
|
|
368
368
|
A human-readable description of the model.
|
|
369
369
|
equations : Sequence[Equation], optional
|
|
370
370
|
A list or sequence of Equation objects that define the constraints of the model.
|
|
371
|
+
No equations are used by default.
|
|
371
372
|
problem : Problem | str, optional
|
|
372
373
|
The type of mathematical problem to solve (e.g., 'LP', 'MIP', 'NLP').
|
|
373
374
|
Default is Problem.MIP.
|
|
@@ -380,7 +381,7 @@ class Model:
|
|
|
380
381
|
Used for defining complementarity problems (MCP). Maps equations to their complementary variables.
|
|
381
382
|
limited_variables : Sequence[ImplicitVariable], optional
|
|
382
383
|
Allows limiting the domain of variables included in the model to a specific subset.
|
|
383
|
-
external_module: str, optional
|
|
384
|
+
external_module : str, optional
|
|
384
385
|
The name of an external module file (e.g., 'my_external.dll' or 'my_external.so')
|
|
385
386
|
where external equations are implemented.
|
|
386
387
|
|
|
@@ -388,9 +389,21 @@ class Model:
|
|
|
388
389
|
--------
|
|
389
390
|
>>> import gamspy as gp
|
|
390
391
|
>>> m = gp.Container()
|
|
391
|
-
>>>
|
|
392
|
-
>>>
|
|
393
|
-
>>>
|
|
392
|
+
>>> i = gp.Set(m, description="items")
|
|
393
|
+
>>> p = gp.Parameter(m, description="profits", domain=i)
|
|
394
|
+
>>> w = gp.Parameter(m, description="weights", domain=i)
|
|
395
|
+
>>> c = gp.Parameter(m, description="capacity")
|
|
396
|
+
>>> x = gp.Variable(m, domain=i, type=gp.VariableType.BINARY)
|
|
397
|
+
>>> capacity_restriction = gp.Equation(m, definition=gp.Sum(i, w[i] * x[i]) <= c)
|
|
398
|
+
|
|
399
|
+
>>> # Instantiate the Model
|
|
400
|
+
>>> knapsack = gp.Model(
|
|
401
|
+
... m,
|
|
402
|
+
... equations=m.getEquations(), # Automatically grabs all equations in the container
|
|
403
|
+
... problem=gp.Problem.MIP, # Mixed Integer Program
|
|
404
|
+
... sense=gp.Sense.MAX, # Maximize profit
|
|
405
|
+
... objective=gp.Sum(i, p[i] * x[i]),
|
|
406
|
+
... )
|
|
394
407
|
|
|
395
408
|
"""
|
|
396
409
|
|
|
@@ -1214,8 +1227,19 @@ class Model:
|
|
|
1214
1227
|
>>> v = gp.Variable(m, "v")
|
|
1215
1228
|
>>> e = gp.Equation(m, "e", definition= v == 5)
|
|
1216
1229
|
>>> my_model = gp.Model(m, "my_model", problem="LP", equations=[e])
|
|
1217
|
-
|
|
1218
|
-
|
|
1230
|
+
|
|
1231
|
+
|
|
1232
|
+
Convert the model into scalar GAMS format.
|
|
1233
|
+
|
|
1234
|
+
|
|
1235
|
+
>>> my_model.convert("tmp", gp.FileFormat.GAMS) # doctest: +SKIP
|
|
1236
|
+
|
|
1237
|
+
|
|
1238
|
+
Add conversion options
|
|
1239
|
+
|
|
1240
|
+
|
|
1241
|
+
>>> options = gp.ConvertOptions(GDXNames=False)
|
|
1242
|
+
>>> my_model.convert("jacobian", file_format=gp.FileFormat.GDXJacobian, options=options) # doctest: +SKIP
|
|
1219
1243
|
|
|
1220
1244
|
"""
|
|
1221
1245
|
path = Path(path)
|
|
@@ -1277,6 +1301,33 @@ class Model:
|
|
|
1277
1301
|
Returns
|
|
1278
1302
|
-------
|
|
1279
1303
|
str
|
|
1304
|
+
|
|
1305
|
+
Examples
|
|
1306
|
+
--------
|
|
1307
|
+
>>> import gamspy as gp
|
|
1308
|
+
>>> m = gp.Container()
|
|
1309
|
+
>>> i = gp.Set(m, records=["item1", "item2"])
|
|
1310
|
+
>>> v = gp.Variable(m, domain=i)
|
|
1311
|
+
>>> z = gp.Variable(m)
|
|
1312
|
+
>>> e = gp.Equation(m, domain=i)
|
|
1313
|
+
>>> e[i] = v[i] * z >= 5
|
|
1314
|
+
>>> model = gp.Model(m, "test", equations=[e], problem="NLP", sense="MIN", objective=z)
|
|
1315
|
+
>>> summary = model.solve(options=gp.Options(variable_listing_limit=10))
|
|
1316
|
+
>>> print(model.getVariableListing())
|
|
1317
|
+
v(item1)
|
|
1318
|
+
(.LO, .L, .UP, .M = -INF, 0, +INF, 0)
|
|
1319
|
+
(0) e(item1)
|
|
1320
|
+
<BLANKLINE>
|
|
1321
|
+
v(item2)
|
|
1322
|
+
(.LO, .L, .UP, .M = -INF, 0, +INF, 0)
|
|
1323
|
+
(0) e(item2)
|
|
1324
|
+
<BLANKLINE>
|
|
1325
|
+
z
|
|
1326
|
+
(.LO, .L, .UP, .M = -INF, 0, +INF, 0)
|
|
1327
|
+
(0) e(item1)
|
|
1328
|
+
(0) e(item2)
|
|
1329
|
+
<BLANKLINE>
|
|
1330
|
+
|
|
1280
1331
|
"""
|
|
1281
1332
|
if not hasattr(self, "_variables"):
|
|
1282
1333
|
raise ValidationError(
|
|
@@ -1301,7 +1352,7 @@ class Model:
|
|
|
1301
1352
|
>>> # ... define model ...
|
|
1302
1353
|
>>> model = gp.Model(m, "my_model", problem="LP", equations=[])
|
|
1303
1354
|
>>> # In a separate thread or signal handler:
|
|
1304
|
-
>>>
|
|
1355
|
+
>>> model.interrupt() # doctest: +SKIP
|
|
1305
1356
|
|
|
1306
1357
|
"""
|
|
1307
1358
|
self.container._interrupt()
|
|
@@ -1415,12 +1466,82 @@ class Model:
|
|
|
1415
1466
|
|
|
1416
1467
|
Examples
|
|
1417
1468
|
--------
|
|
1469
|
+
>>> import sys
|
|
1470
|
+
>>> import os
|
|
1418
1471
|
>>> import gamspy as gp
|
|
1419
1472
|
>>> m = gp.Container()
|
|
1420
|
-
>>>
|
|
1421
|
-
>>>
|
|
1422
|
-
>>>
|
|
1423
|
-
>>>
|
|
1473
|
+
>>> i = gp.Set(m, description="items")
|
|
1474
|
+
>>> p = gp.Parameter(m, description="profits", domain=i)
|
|
1475
|
+
>>> w = gp.Parameter(m, description="weights", domain=i)
|
|
1476
|
+
>>> c = gp.Parameter(m, description="capacity")
|
|
1477
|
+
>>> x = gp.Variable(m, domain=i, type=gp.VariableType.BINARY)
|
|
1478
|
+
>>> capacity_restriction = gp.Equation(m, definition=gp.Sum(i, w[i] * x[i]) <= c)
|
|
1479
|
+
>>> knapsack = gp.Model(
|
|
1480
|
+
... m,
|
|
1481
|
+
... equations=m.getEquations(), # Automatically grabs all equations in the container
|
|
1482
|
+
... problem=gp.Problem.MIP, # Mixed Integer Program
|
|
1483
|
+
... sense=gp.Sense.MAX, # Maximize profit
|
|
1484
|
+
... objective=gp.Sum(i, p[i] * x[i]),
|
|
1485
|
+
... )
|
|
1486
|
+
|
|
1487
|
+
|
|
1488
|
+
Basic usage
|
|
1489
|
+
|
|
1490
|
+
|
|
1491
|
+
>>> knapsack.solve() # doctest: +SKIP
|
|
1492
|
+
|
|
1493
|
+
|
|
1494
|
+
Change solver
|
|
1495
|
+
|
|
1496
|
+
|
|
1497
|
+
>>> knapsack.solve(solver="CPLEX") # doctest: +SKIP
|
|
1498
|
+
|
|
1499
|
+
|
|
1500
|
+
Redirect output
|
|
1501
|
+
|
|
1502
|
+
|
|
1503
|
+
>>> knapsack.solve(output=sys.stdout) # doctest: +SKIP
|
|
1504
|
+
|
|
1505
|
+
|
|
1506
|
+
Add generic solve options
|
|
1507
|
+
|
|
1508
|
+
|
|
1509
|
+
>>> knapsack.solve(options=gp.Options(iteration_limit=2)) # doctest: +SKIP
|
|
1510
|
+
|
|
1511
|
+
|
|
1512
|
+
Add solver-specific options
|
|
1513
|
+
|
|
1514
|
+
|
|
1515
|
+
>>> knapsack.solve(solver="CPLEX", solver_options={"preind": "off"}) # doctest: +SKIP
|
|
1516
|
+
|
|
1517
|
+
|
|
1518
|
+
Solve on your own machine
|
|
1519
|
+
|
|
1520
|
+
|
|
1521
|
+
>>> knapsack.solve() # doctest: +SKIP
|
|
1522
|
+
|
|
1523
|
+
|
|
1524
|
+
Solve on GAMS Engine
|
|
1525
|
+
|
|
1526
|
+
|
|
1527
|
+
>>> client = gp.EngineClient(
|
|
1528
|
+
... host=os.getenv("ENGINE_URL", "https://<host_link>"),
|
|
1529
|
+
... username=os.getenv("ENGINE_USER"),
|
|
1530
|
+
... password=os.getenv("ENGINE_PASSWORD"),
|
|
1531
|
+
... namespace=os.getenv("ENGINE_NAMESPACE"),
|
|
1532
|
+
... )
|
|
1533
|
+
>>> knapsack.solve(backend="engine", client=client) # doctest: +SKIP
|
|
1534
|
+
|
|
1535
|
+
|
|
1536
|
+
Solve on NEOS Server
|
|
1537
|
+
|
|
1538
|
+
|
|
1539
|
+
>>> client = gp.NeosClient(
|
|
1540
|
+
... email=os.getenv("NEOS_EMAIL"),
|
|
1541
|
+
... username=os.getenv("NEOS_USER"),
|
|
1542
|
+
... password=os.getenv("NEOS_PASSWORD"),
|
|
1543
|
+
... )
|
|
1544
|
+
>>> knapsack.solve(backend="neos", client=client) # doctest: +SKIP
|
|
1424
1545
|
|
|
1425
1546
|
"""
|
|
1426
1547
|
if output is None:
|
|
@@ -1626,7 +1747,7 @@ class Model:
|
|
|
1626
1747
|
>>> v = gp.Variable(m, "v")
|
|
1627
1748
|
>>> e = gp.Equation(m, "e", definition= v == 5)
|
|
1628
1749
|
>>> my_model = gp.Model(m, "my_model", problem="LP", equations=[e])
|
|
1629
|
-
>>> my_model.toGams("tmp") # doctest: +
|
|
1750
|
+
>>> my_model.toGams("tmp") # doctest: +SKIP
|
|
1630
1751
|
================================================================================
|
|
1631
1752
|
GAMS (.gms) file has been generated under ...
|
|
1632
1753
|
================================================================================
|
|
@@ -1668,7 +1789,7 @@ class Model:
|
|
|
1668
1789
|
>>> v = gp.Variable(m, "v")
|
|
1669
1790
|
>>> e = gp.Equation(m, "e", definition= v == 5)
|
|
1670
1791
|
>>> my_model = gp.Model(m, "my_model", problem="LP", equations=[e])
|
|
1671
|
-
>>> my_model.toLatex("tmp") # doctest: +
|
|
1792
|
+
>>> my_model.toLatex("tmp") # doctest: +SKIP
|
|
1672
1793
|
================================================================================
|
|
1673
1794
|
LaTeX (.tex) file has been generated under ...
|
|
1674
1795
|
================================================================================
|
gamspy/_model_instance.py
CHANGED
|
@@ -318,8 +318,8 @@ class ModelInstance:
|
|
|
318
318
|
gams_file.write(scenario_str)
|
|
319
319
|
|
|
320
320
|
# Write pf file
|
|
321
|
-
|
|
322
|
-
options.
|
|
321
|
+
hidden_options = self._prepare_hidden_options()
|
|
322
|
+
options._set_hidden_options(hidden_options)
|
|
323
323
|
options.log_file = os.path.join(self.container.working_directory, "gamslog.dat")
|
|
324
324
|
options._export(self.pf_file, self.output)
|
|
325
325
|
|
|
@@ -632,9 +632,9 @@ class ModelInstance:
|
|
|
632
632
|
|
|
633
633
|
return will_be_modified
|
|
634
634
|
|
|
635
|
-
def
|
|
635
|
+
def _prepare_hidden_options(self) -> dict:
|
|
636
636
|
scrdir = self.container._process_directory
|
|
637
|
-
|
|
637
|
+
hidden_options = {
|
|
638
638
|
"trace": self.trace_file,
|
|
639
639
|
"input": self.gms_file,
|
|
640
640
|
"output": self.lst_file,
|
|
@@ -646,11 +646,11 @@ class ModelInstance:
|
|
|
646
646
|
"solvercntr": self.solver_control_file,
|
|
647
647
|
}
|
|
648
648
|
if self.container._network_license:
|
|
649
|
-
|
|
649
|
+
hidden_options["netlicense"] = os.path.join(
|
|
650
650
|
self.container._process_directory, "gamslice.dat"
|
|
651
651
|
)
|
|
652
652
|
|
|
653
|
-
return
|
|
653
|
+
return hidden_options
|
|
654
654
|
|
|
655
655
|
def _get_columns_to_drop(self, attr: str) -> list[str]:
|
|
656
656
|
attr_map = {
|
gamspy/_options.py
CHANGED
|
@@ -5,6 +5,7 @@ from pathlib import Path
|
|
|
5
5
|
from typing import TYPE_CHECKING, Any, Literal
|
|
6
6
|
|
|
7
7
|
from pydantic import BaseModel, ConfigDict
|
|
8
|
+
from typing_extensions import override
|
|
8
9
|
|
|
9
10
|
from gamspy.exceptions import ValidationError
|
|
10
11
|
|
|
@@ -381,8 +382,9 @@ class Options(BaseModel):
|
|
|
381
382
|
zero_rounding_threshold: float | None = None
|
|
382
383
|
report_underflow: bool | None = None
|
|
383
384
|
|
|
385
|
+
@override
|
|
384
386
|
def model_post_init(self, context: Any) -> None:
|
|
385
|
-
self.
|
|
387
|
+
self._hidden_options: dict[str, Any] = {}
|
|
386
388
|
self._debug_options: dict[str, Any] = {}
|
|
387
389
|
self._solver: str | None = None
|
|
388
390
|
self._problem: str | None = None
|
|
@@ -502,9 +504,9 @@ class Options(BaseModel):
|
|
|
502
504
|
else:
|
|
503
505
|
self._solver_options_file = "0"
|
|
504
506
|
|
|
505
|
-
def
|
|
507
|
+
def _set_hidden_options(self, options: dict) -> None:
|
|
506
508
|
"""Set extra options of the backend"""
|
|
507
|
-
self.
|
|
509
|
+
self._hidden_options = options
|
|
508
510
|
|
|
509
511
|
def _set_debug_options(self, options: dict) -> None:
|
|
510
512
|
"""Set debugging options"""
|
|
@@ -606,7 +608,7 @@ class Options(BaseModel):
|
|
|
606
608
|
all_options["optfile"] = self._solver_options_file
|
|
607
609
|
|
|
608
610
|
# Extra options
|
|
609
|
-
all_options.update(**self.
|
|
611
|
+
all_options.update(**self._hidden_options)
|
|
610
612
|
all_options.update(**self._debug_options)
|
|
611
613
|
|
|
612
614
|
if self._frame is not None:
|
|
@@ -753,7 +755,7 @@ class ConvertOptions(BaseModel):
|
|
|
753
755
|
>>> eq[i] = x[i] >= 1
|
|
754
756
|
>>> model = gp.Model(m, "test_model", equations=[eq], problem="LP", sense="min", objective=gp.Sum(i, x[i]))
|
|
755
757
|
>>> options = gp.ConvertOptions(GDXNames=False)
|
|
756
|
-
>>>
|
|
758
|
+
>>> model.convert("jacobian", file_format=gp.FileFormat.GDXJacobian, options=options) # doctest: +SKIP
|
|
757
759
|
|
|
758
760
|
"""
|
|
759
761
|
|
gamspy/_symbols/symbol.py
CHANGED
gamspy/_workspace.py
CHANGED
|
@@ -18,7 +18,7 @@ def validate_arguments(
|
|
|
18
18
|
if working_directory == "":
|
|
19
19
|
raise ValidationError("`working_directory` cannot be an empty string.")
|
|
20
20
|
|
|
21
|
-
# Validate
|
|
21
|
+
# Validate debugging_level
|
|
22
22
|
if not isinstance(debugging_level, str) or debugging_level not in DEBUGGING_LEVELS:
|
|
23
23
|
raise ValidationError(f"debugging level must be one of {DEBUGGING_LEVELS}")
|
|
24
24
|
|
gamspy/formulations/piecewise.py
CHANGED
|
@@ -280,7 +280,7 @@ def _generate_ray(
|
|
|
280
280
|
return x_var, b_var, eqs
|
|
281
281
|
|
|
282
282
|
|
|
283
|
-
def
|
|
283
|
+
def _points_to_intervals(
|
|
284
284
|
x_points: typing.Sequence[int | float],
|
|
285
285
|
y_points: typing.Sequence[int | float],
|
|
286
286
|
discontinuous_points: typing.Sequence[int],
|
|
@@ -422,7 +422,7 @@ def pwl_interval_formulation(
|
|
|
422
422
|
combined_indices = list({*discontinuous_indices, *none_indices})
|
|
423
423
|
equations = []
|
|
424
424
|
|
|
425
|
-
intervals =
|
|
425
|
+
intervals = _points_to_intervals(x_points, y_points, combined_indices)
|
|
426
426
|
lowerbounds_input = []
|
|
427
427
|
upperbounds_input = []
|
|
428
428
|
slopes_input = []
|
gamspy/math/activation.py
CHANGED
|
@@ -48,6 +48,22 @@ def tanh(x: Variable) -> tuple[Variable, list[Equation]]:
|
|
|
48
48
|
Convenience wrapper that uses gamspy.math.tanh. Unlike gamspy.math.tanh,
|
|
49
49
|
this function creates a new variable and the equation that
|
|
50
50
|
sets it to follow formulations structure.
|
|
51
|
+
|
|
52
|
+
Parameters
|
|
53
|
+
----------
|
|
54
|
+
x : Variable
|
|
55
|
+
|
|
56
|
+
Returns
|
|
57
|
+
-------
|
|
58
|
+
tuple[Variable, list[Equation]]
|
|
59
|
+
|
|
60
|
+
Examples
|
|
61
|
+
--------
|
|
62
|
+
>>> import gamspy as gp
|
|
63
|
+
>>> m = gp.Container()
|
|
64
|
+
>>> v1 = gp.Variable(m)
|
|
65
|
+
>>> v2, eqs = gp.math.activation.tanh(v1)
|
|
66
|
+
|
|
51
67
|
"""
|
|
52
68
|
y = x.container.addVariable(domain=x.domain)
|
|
53
69
|
set_y = x.container.addEquation(domain=x.domain)
|