from __future__ import annotations
import itertools
import os
import threading
import weakref
from typing import TYPE_CHECKING, Any, cast, no_type_check
import numpy as np
import pandas as pd
from gams.core.gdx import GMS_DT_PAR
import gamspy as gp
import gamspy._algebra.condition as condition
import gamspy._algebra.expression as expression
import gamspy._algebra.operable as operable
import gamspy._algebra.operation as operation
import gamspy._symbols.implicits as implicits
import gamspy._validation as validation
import gamspy.utils as utils
from gamspy._records_ingestion import ParameterIngestor
from gamspy._symbols.base import RecordSymbol
from gamspy._symbols.equals import equals_parameter
from gamspy._symbols.generate_records import generate_records_parameter
from gamspy._symbols.pivot import pivot_parameter
from gamspy.exceptions import ValidationError
if TYPE_CHECKING:
from collections.abc import Callable
from scipy.sparse import coo_matrix
from gamspy import Container
from gamspy._algebra.condition import Condition
from gamspy._algebra.expression import Expression
from gamspy._algebra.number import Number
from gamspy._algebra.operation import Operation
from gamspy._extrinsic import ExtrinsicFunction
from gamspy._symbols.implicits import ImplicitParameter
from gamspy._types import DomainType, IndexType, ParameterRecordsType
from gamspy.math.misc import MathOp
[docs]
class Parameter(operable.Operable, RecordSymbol):
"""
Represents a Parameter symbol in GAMS.
Parameters are used to hold data (scalars, vectors, or multi-dimensional arrays).
See https://gamspy.readthedocs.io/en/latest/user/basics/parameter.html
Parameters
----------
container : Container
The Container object that this parameter belongs to.
name : str, optional
Name of the parameter. If not provided, a unique name is generated automatically.
domain : DomainType, optional
The domain of the parameter. Can be a list of Sets/Aliases, a single Set/Alias,
or strings representing set names. Use "*" for the universe set. Default is [] (scalar).
records : Sequence | np.ndarray | int | float | pd.DataFrame | pd.Series, optional
Initial values to populate the parameter. Can be a scalar, a list, a numpy array, or a pandas DataFrame.
domain_forwarding : bool | list[bool], optional
If True, adding records to this parameter will implicitly add new elements to the
domain sets (if they are dynamic). Default is False.
description : str, optional
A human-readable description of the parameter.
uels_on_axes : bool, optional
If True, implies that the Unique Element Labels (UELs) for the domain are
contained in the axes (index/columns) of the provided `records` object
(e.g., pandas DataFrame). Default is False.
is_miro_input : bool, optional
If True, flags this parameter as an input symbol for GAMS MIRO. Default is False.
is_miro_output : bool, optional
If True, flags this parameter as an output symbol for GAMS MIRO. Default is False.
is_miro_table : bool, optional
If True, flags this parameter as a table symbol for GAMS MIRO. Default is False.
Examples
--------
>>> import gamspy as gp
>>> m = gp.Container()
>>> i = gp.Set(m, "i", records=['i1', 'i2'])
>>> a = gp.Parameter(m, "a", domain=[i], records=[['i1', 1], ['i2', 2]], description="Input data")
"""
@classmethod
def _constructor_bypass(
cls,
container: Container,
name: str,
domain: DomainType | None = None,
records: ParameterRecordsType | None = None,
description: str = "",
) -> Parameter:
obj = object.__new__(cls)
# legacy gtp attributes
## set private properties directly
obj._container = cast(
"Container",
weakref.proxy(container)
if not isinstance(container, weakref.ProxyType)
else container,
)
obj.name = name
obj._domain = obj._normalize_domain(obj._container, domain)
obj._domain_forwarding = False
obj._description = description
obj._records = records
obj._gams_type = GMS_DT_PAR
obj._gams_subtype = 0
obj._container._data.update({name: obj})
# gamspy attributes
obj._domain_violations = None
obj.where = condition.Condition(obj)
obj._latex_name = name.replace("_", r"\_")
obj.container._add_statement(obj)
obj._metadata = {}
obj._should_load_from_gams = False
obj._should_unload_to_gams = False
## miro support
obj._is_miro_input = False
obj._is_miro_output = False
obj._is_miro_table = False
return obj
def __new__(
cls,
container: Container | None = None,
name: str | None = None,
domain: DomainType | None = None,
records: ParameterRecordsType | None = None,
domain_forwarding: bool | list[bool] = False,
description: str = "",
uels_on_axes: bool = False,
is_miro_input: bool = False,
is_miro_output: bool = False,
is_miro_table: bool = False,
):
if container is not None and not isinstance(container, gp.Container):
raise TypeError(
f"Container must of type `Container` but found {type(container)}"
)
if name is None:
return object.__new__(cls)
else:
if not isinstance(name, str):
raise TypeError(f"Name must of type `str` but found {type(name)}")
try:
if not container:
container = gp._ctx_managers[
(os.getpid(), threading.get_native_id())
]
symbol = container._data[name]
except KeyError:
return object.__new__(cls)
if isinstance(symbol, cls):
return symbol
raise TypeError(
f"Cannot overwrite symbol `{name}` in container"
" because it is not a Parameter object"
)
def __init__(
self,
container: Container | None = None,
name: str | None = None,
domain: DomainType | None = None,
records: ParameterRecordsType | None = None,
domain_forwarding: bool | list[bool] = False,
description: str = "",
uels_on_axes: bool = False,
is_miro_input: bool = False,
is_miro_output: bool = False,
is_miro_table: bool = False,
):
self._metadata: dict[str, Any] = {}
if (is_miro_input or is_miro_output) and name is None:
raise ValidationError("Please specify a name for miro symbols.")
# miro support
self._is_miro_input = is_miro_input
self._is_miro_output = is_miro_output
self._is_miro_table = is_miro_table
self._is_miro_symbol = is_miro_input or is_miro_output or is_miro_table
self._domain_violations = None
# does symbol exist
has_symbol = False
if isinstance(getattr(self, "container", None), gp.Container):
has_symbol = True
if has_symbol:
domain = self._normalize_domain(self.container, domain)
if any(d1 != d2 for d1, d2 in itertools.zip_longest(self._domain, domain)):
raise ValueError(
"Cannot overwrite symbol in container unless symbol"
" domains are equal"
)
if self._domain_forwarding != domain_forwarding:
raise ValueError(
"Cannot overwrite symbol in container unless"
" 'domain_forwarding' is left unchanged"
)
# reset some properties
if description != "":
self._description = description
self._records: pd.DataFrame | None = None
previous_state = self._container._options.miro_protect
self._container._options.miro_protect = False
if records is not None:
self.setRecords(records, uels_on_axes=uels_on_axes)
self._container._options.miro_protect = previous_state
else:
if container is None:
try:
container = gp._ctx_managers[
(os.getpid(), threading.get_native_id())
]
except KeyError as e:
raise ValidationError("Parameter requires a container.") from e
self._container = cast("Container", weakref.proxy(container))
if name is not None:
name = validation.validate_name(name)
if is_miro_input or is_miro_output:
name = name.lower()
else:
name = self._container._get_symbol_name(prefix="p")
self.name = name
domain = self._normalize_domain(self.container, domain)
self._domain = self._validate_domain(domain)
self._domain_forwarding = domain_forwarding
self._description = description
self._records = None
self._gams_type = GMS_DT_PAR
self._gams_subtype = 0
self._latex_name = self.name.replace("_", r"\_")
self._should_load_from_gams = False
self._should_unload_to_gams = False
self._container._data.update({name: self})
if is_miro_input:
self._already_loaded = False
self._container._miro_input_symbols.append(self.name)
if is_miro_output:
self._container._miro_output_symbols.append(self.name)
validation.validate_container(self, self._domain)
self.where = condition.Condition(self)
self._assignment: Expression | None = None
self._container._add_statement(self)
previous_state = self._container._options.miro_protect
self._container._options.miro_protect = False
if records is not None:
self._setRecords(records, uels_on_axes=uels_on_axes)
if self.dimension == 0 and not self._is_miro_symbol:
self._should_unload_to_gams = False
else:
if self._is_miro_symbol:
self._should_unload_to_gams = True
self._container._synch_with_gams()
self._container._options.miro_protect = previous_state
def _serialize(self) -> dict:
info: dict[str, Any] = {
"_domain_forwarding": self._domain_forwarding,
"_is_miro_input": self._is_miro_input,
"_is_miro_output": self._is_miro_output,
"_is_miro_table": self._is_miro_table,
"_metadata": self._metadata,
}
if self._assignment is not None:
info["_assignment"] = self._assignment.getDeclaration()
return info
def _deserialize(self, info: dict) -> None:
for key, value in info.items():
if key == "_assignment":
left, right = value.split(" = ")
value = expression.Expression(left, "=", right[:-1])
setattr(self, key, value)
# Relink domain symbols
new_domain = []
for elem in self._domain:
if elem == "*":
new_domain.append(elem)
continue
new_domain.append(self._container[elem.name])
self._domain = new_domain
def __getitem__(self, indices: IndexType) -> ImplicitParameter:
domain = validation.validate_domain(self, indices)
return implicits.ImplicitParameter(self, name=self.name, domain=domain)
def __setitem__(
self,
indices: IndexType,
rhs: Operation
| Expression
| MathOp
| Condition
| Parameter
| ImplicitParameter
| float
| int
| Number
| ExtrinsicFunction,
):
# self[domain] = rhs
domain = validation.validate_domain(self, indices)
if self._is_miro_input and self._container._options.miro_protect:
raise ValidationError(
f"Cannot assign to protected miro input symbol {self.name}. `miro_protect`"
" attribute of the container can be set to False to allow"
" assigning to MIRO input symbols"
)
if isinstance(rhs, float):
rhs = utils._map_special_values(rhs)
statement = expression.Expression(
implicits.ImplicitParameter(self, name=self.name, domain=domain),
"=",
rhs,
)
# Cannot validate definition if we are in a gp.Loop since the control indices can be provided by the gp.Loop
if not self._container._in_loop:
statement._validate_definition(utils._unpack(domain))
self._container._add_statement(statement)
self._assignment = statement
self.container._synch_with_gams()
self._should_load_from_gams = True
def __eq__(self, other):
op = "eq"
if isinstance(
other,
(
implicits.ImplicitVariable,
expression.Expression,
operation.Operation,
),
):
op = "=e="
return expression.Expression(self, op, other)
def __ne__(self, other):
return expression.Expression(self, "ne", other)
def __repr__(self) -> str:
return f"Parameter(name='{self.name}', domain={self.domain})"
@property
def T(self) -> ImplicitParameter:
"""
Alias for the `.t()` method.
Returns
-------
ImplicitParameter
"""
return self.t()
def t(self) -> ImplicitParameter:
"""
Returns an ImplicitParameter derived from this
parameter by swapping its last two indices. This operation
does not generate a new parameter in GAMS.
Examples
--------
>>> import gamspy as gp
>>> m = gp.Container()
>>> i = gp.Set(m, "i", records=['i1','i2'])
>>> j = gp.Set(m, "j", records=['j1','j2'])
>>> v = gp.Parameter(m, "v", domain=[i, j])
>>> v_t = v.t()
>>> v_t.domain
[Set(name='j', domain=['*']), Set(name='i', domain=['*'])]
>>> v_t[i, j] # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
gamspy.exceptions.ValidationError:
>>> v_t["j1", "i1"].gamsRepr()
'v("i1","j1")'
"""
from gamspy.math.matrix import permute
dims = list(range(len(self.domain)))
if len(dims) < 2:
raise ValidationError(
"Parameter must contain at least 2 dimensions to transpose"
)
x = dims[-1]
dims[-1] = dims[-2]
dims[-2] = x
return permute(self, dims) # type: ignore
@property
def _attributes(self):
return ["value"]
@property
def summary(self) -> dict:
return {
"name": self.name,
"description": self.description,
"domain": self.domain_names,
"domain_type": self.domain_type,
"dimension": self.dimension,
"number_records": self.number_records,
}
[docs]
def toValue(self) -> float:
"""
Returns the numerical value of a scalar Parameter.
Returns
-------
float | None
The floating-point value of the scalar parameter.
Raises
------
TypeError
If the parameter is not a scalar (dimension > 0).
Examples
--------
>>> import gamspy as gp
>>> m = gp.Container()
>>> p = gp.Parameter(m, name="p", records=42.5)
>>> p.toValue()
np.float64(42.5)
"""
from gamspy._symbols.utils import toValueParameter
return toValueParameter(self)
[docs]
def toList(self) -> list:
"""
Converts the records of the Parameter to a Python list.
Returns
-------
list | None
A list containing the parameter's data. For scalars, it returns a list with a single
numerical value. For multi-dimensional parameters, it returns a list of tuples where
the last element of each tuple is the value. Returns an empty list if there are no records.
Examples
--------
>>> import gamspy as gp
>>> import numpy as np
>>> m = gp.Container()
>>> i = gp.Set(m, name="i", records=["seattle", "san-diego"])
>>> d = gp.Parameter(m, name="d", domain=[i], records=np.array([10, 25]))
>>> d.toList()
[('seattle', 10.0), ('san-diego', 25.0)]
"""
from gamspy._symbols.utils import toListParameter
return toListParameter(self)
[docs]
def toDict(self, orient: str | None = None) -> dict | None:
"""
Converts the records of a non-scalar Parameter to a Python dictionary.
Parameters
----------
orient : str | None, optional
The format of the dictionary. Options are:
- "natural" (default): Maps domain elements to values (e.g., `{'A': 10.0}`).
For multi-dimensional parameters, keys are tuples (e.g., `{(A, X): 10.0}`).
- "columns": Returns a dictionary of columns (e.g., `{'i': {0: 'A'}, 'value': {0: 10.0}}`).
Returns
-------
dict | None
A dictionary containing the parameter's data, or None if there are no records.
Raises
------
TypeError
If the parameter is a scalar.
Examples
--------
>>> import gamspy as gp
>>> import numpy as np
>>> m = gp.Container()
>>> i = gp.Set(m, name="i", records=["seattle", "san-diego"])
>>> p = gp.Parameter(m, name="p", domain=[i], records=np.array([10.0, 20.0]))
>>> p.toDict()
{'seattle': 10.0, 'san-diego': 20.0}
"""
from gamspy._symbols.utils import toDictParameter
return toDictParameter(self, orient=orient)
# TODO: Legacy function from GTP. Pay the technical debt.
[docs]
@no_type_check
def toSparseCoo(self) -> coo_matrix | None:
"""
Converts the parameter records to a SciPy sparse COOrdinate format (coo_matrix).
This method is only available for parameters with 2 or fewer dimensions.
For scalar parameters (0D), it returns a 1x1 matrix. For 1D parameters,
it returns a 1xN matrix. For 2D parameters, it returns an MxN matrix.
Returns
-------
coo_matrix | None
A SciPy sparse COO matrix containing the parameter values. Returns None
if there are no records.
Raises
------
ValueError
If the parameter has a dimension greater than 2.
Examples
--------
>>> import gamspy as gp
>>> import numpy as np
>>> m = gp.Container()
>>> i = gp.Set(m, name="i", records=["A", "B"])
>>> j = gp.Set(m, name="j", records=["X", "Y"])
>>> p = gp.Parameter(m, name="p", domain=[i, j])
>>> p.setRecords(np.array([[1, 0], [0, 2]]))
>>> sparse_mat = p.toSparseCoo() # doctest +SKIP
"""
from scipy.sparse import coo_matrix
if self.records is None:
return None
if self.is_scalar:
row, col, m, n = [0], [0], 1, 1
elif self.dimension == 1:
if self.domain_type == "regular":
col = (
self.records.iloc[:, 0]
.map(self.domain[0]._getUELCodes(0, ignore_unused=True))
.to_numpy(dtype=int)
)
else:
col = self.records.iloc[:, 0].cat.codes.to_numpy(dtype=int)
row = np.zeros(len(col), dtype=int)
m, *n_arr = self.shape
assert not n_arr
n, m = m, 1
elif self.dimension == 2:
if self.domain_type == "regular":
row = (
self.records.iloc[:, 0]
.map(self.domain[0]._getUELCodes(0, ignore_unused=True))
.to_numpy(dtype=int)
)
col = (
self.records.iloc[:, 1]
.map(self.domain[1]._getUELCodes(0, ignore_unused=True))
.to_numpy(dtype=int)
)
else:
row = self.records.iloc[:, 0].cat.codes.to_numpy(dtype=int)
col = self.records.iloc[:, 1].cat.codes.to_numpy(dtype=int)
m, n = self.shape
else:
raise ValueError(
"Sparse coo_matrix formats are only available for data that has dimension <= 2"
)
return coo_matrix(
(self.records.iloc[:, -1].to_numpy(dtype=float), (row, col)),
shape=(m, n),
dtype=float,
)
# TODO: Legacy function from GTP. Pay the technical debt.
[docs]
@no_type_check
def toDense(self) -> np.ndarray | None:
"""
Convert symbol records to a dense numpy.array format
Returns
-------
ndarray | None
A numpy array with symbol records, None if no records were assigned
Examples
--------
>>> import numpy as np
>>> import gamspy as gp
>>> m = gp.Container()
>>> j = gp.Set(m, "j", records=["new-york", "chicago", "topeka"])
>>> s = gp.Parameter(m, "s", [j], records=np.array([3,4,5]))
>>> print(s.toDense())
[3. 4. 5.]
"""
if self.records is None:
return None
if self.is_scalar:
return self.records.to_numpy(dtype=float).reshape(self.shape)
if self.domain_type == "regular":
for symobj in self.domain:
data_cats = symobj.records.iloc[:, 0].unique().tolist()
cats = symobj.records.iloc[:, 0].cat.categories.tolist()
if data_cats != cats[: len(data_cats)]:
raise ValidationError(
f"`toDense` requires that UEL data order of domain set `{symobj.name}` must be "
"equal be equal to UEL category order (i.e., the order that set elements "
"appear in rows of the dataframe and the order set elements are specified by the categorical). "
)
else:
for n in range(self.dimension):
if any(code == -1 for code in self.records.iloc[:, n].cat.codes):
raise ValidationError(
f"Invalid category detected in dimension `{n}` (code == -1), "
"cannot create array until all categories are properly resolved"
)
data_cats = self.records.iloc[:, n].unique().tolist()
cats = self.records.iloc[:, n].cat.categories.tolist()
if data_cats != cats[: len(data_cats)]:
raise ValidationError(
"`toDense` requires (for 'relaxed' symbols) that UEL data order must be "
"equal be equal to UEL category order (i.e., the order that set elements "
"appear in rows of the dataframe and the order set elements are specified by the categorical). "
)
if self.domain_type == "regular":
idx = [
self.records.iloc[:, n]
.map(domainobj._getUELCodes(0, ignore_unused=True))
.to_numpy(dtype=int)
for n, domainobj in enumerate(self.domain)
]
else:
idx = [
self.records.iloc[:, n].cat.codes.to_numpy(dtype=int)
for n, domainobj in enumerate(self.domain)
]
a = np.zeros(self.shape)
val = self.records.iloc[:, -1].to_numpy(dtype=float)
a[tuple(idx)] = val
return a
[docs]
def pivot(
self,
index: str | list | None = None,
columns: str | list | None = None,
fill_value: int | float | str | None = None,
) -> pd.DataFrame:
"""
Convenience function to pivot records into a new shape (only symbols with >1D can be pivoted).
Parameters
----------
index : str | list, optional
If index is None then it is set to dimensions [0..dimension-1], by default None
columns : str | list, optional
If columns is None then it is set to the last dimension, by default None
fill_value : int | float | str, optional
Missing values in the pivot will take the value provided by fill_value, by default None
Returns
-------
pd.DataFrame
The pivoted DataFrame representing the parameter records.
Examples
--------
>>> import gamspy as gp
>>> m = gp.Container()
>>> i = gp.Set(m, name="i", records=["A", "B"])
>>> j = gp.Set(m, name="j", records=["X", "Y"])
>>> p = gp.Parameter(m, name="p", domain=[i, j], records=[("A", "X", 10), ("B", "Y", 20)])
>>> df = p.pivot()
"""
return pivot_parameter(self, index, columns, fill_value)
[docs]
def generateRecords(
self,
density: int | float | list | None = None,
func: Callable | None = None,
seed: int | None = None,
) -> None:
"""
Generates records with the Cartesian product of all domain sets.
Parameters
----------
density : int | float | list, optional
Takes any value on the interval [0,1]. If density is <1 then randomly selected records will be removed.
`density` will accept a `list` of length `dimension` which allows users to specify a density per symbol dimension,
by default None.
func : Callable, optional
Functions to generate the records, by default None; numpy.random.uniform(0,1)
seed : int, optional
Random number state can be set with `seed` argument, by default None
Examples
--------
>>> import gamspy as gp
>>> m = gp.Container()
>>> i = gp.Set(m, name="i", records=["A", "B"])
>>> p = gp.Parameter(m, name="p", domain=[i])
>>> p.generateRecords(seed=42)
"""
generate_records_parameter(self, density, func, seed)
[docs]
def equals(
self,
other: Parameter,
check_meta_data: bool = True,
rtol: int | float | None = None,
atol: int | float | None = None,
) -> bool:
"""
Used to compare the symbol to another symbol
Parameters
----------
other : Parameter
Other Symbol to compare with
check_uels : bool, optional
If True, check both used and unused UELs and confirm same order, otherwise only check used UELs in data and do not check UEL order. by default True
check_meta_data : bool, optional
If True, check that symbol name and description are the same, otherwise skip. by default True
rtol : int | float, optional
relative tolerance, by default None
atol : int | float, optional
absolute tolerance, by default None
Returns
-------
bool
True if symbols are equal, False otherwise
Examples
--------
>>> import gamspy as gp
>>> m = gp.Container()
>>> p1 = gp.Parameter(m, name="p1", records=10.0001)
>>> p2 = gp.Parameter(m, name="p2", records=10.0002)
>>> p1.equals(p2, check_meta_data=False, atol=1e-3)
True
"""
return equals_parameter(self, other, check_meta_data, rtol, atol)
@property
def records(self) -> pd.DataFrame | None:
"""
Returns the records (data) of the Parameter as a DataFrame.
Returns
-------
pd.DataFrame | None
The dataframe containing the parameter's data.
Examples
--------
>>> import gamspy as gp
>>> import numpy as np
>>> m = gp.Container()
>>> i = gp.Set(m, name="i", records=["seattle", "san-diego"])
>>> d = gp.Parameter(m, name="d", domain=[i])
>>> d.setRecords(np.array([10, 25]))
>>> d.toList()
[('seattle', 10.0), ('san-diego', 25.0)]
"""
if self._should_load_from_gams:
self._load_from_gams()
return self._records
@records.setter
def records(self, records: pd.DataFrame | None):
if (
hasattr(self, "_is_miro_input")
and self._is_miro_input
and self._container._options.miro_protect
):
raise ValidationError(
"Cannot assign to protected miro input symbols. `miro_protect`"
" attribute of the container can be set to False to allow"
" assigning to MIRO input symbols"
)
if records is not None and not isinstance(records, pd.DataFrame):
raise TypeError("Symbol 'records' must be type DataFrame")
self._records = records
self._should_unload_to_gams = True
self._handle_domain_forwarding()
def __hash__(self):
return id(self)
def _setRecords(self, records: Any, *, uels_on_axes: bool = False) -> None:
ParameterIngestor(self).ingest(records, uels_on_axes=uels_on_axes)
self._handle_domain_violations()
[docs]
def setRecords(
self, records: ParameterRecordsType | None, uels_on_axes: bool = False
) -> None:
"""
Sets the records (data) of the Parameter.
This is a convenience method to load data into the parameter. It handles various
input formats like scalars, lists, numpy arrays, and pandas DataFrames.
If `uels_on_axes=True`, it assumes that all domain information is contained
in the axes (index/columns) of the pandas object, and the data will be flattened if necessary.
Parameters
----------
records : Sequence | np.ndarray | int | float | pd.DataFrame | pd.Series
The data to load. Common formats:
- Scalar: `10.5`
- List of tuples/lists: `[['i1', 1], ['i2', 2]]`
- numpy array: `np.array([1, 2])`
- pandas DataFrame
uels_on_axes : bool, optional
If True, assumes domain elements are in the axes of the DataFrame. Default is False.
Examples
--------
>>> import gamspy as gp
>>> import numpy as np
>>> m = gp.Container()
>>> i = gp.Set(m, name="i")
>>> i.setRecords(["seattle", "san-diego"])
>>> i.toList()
['seattle', 'san-diego']
"""
if self._is_miro_input and self._container._options.miro_protect:
raise ValidationError(
f"Cannot assign to protected miro input symbol {self.name}. `miro_protect`"
" attribute of the container can be set to False to allow"
" assigning to MIRO input symbols"
)
if records is None:
self._container._add_statement(f"option clear={self.name};")
self._container._synch_with_gams()
self._records = None
elif isinstance(records, (int, float)):
self._container._add_statement(f"{self.name} = {records};")
self._container._synch_with_gams()
self._should_load_from_gams = True
else:
self._setRecords(records, uels_on_axes=uels_on_axes)
self._container._synch_with_gams()
[docs]
def gamsRepr(self) -> str:
"""
Returns the string representation of this Parameter in the GAMS language.
The representation includes the parameter name and its domain (e.g., 'p(i, j)').
Returns
-------
str
The GAMS string representation.
Examples
--------
>>> import gamspy as gp
>>> m = gp.Container()
>>> i = gp.Set(m, "i", records=['i1','i2'])
>>> d = gp.Parameter(m, "d", domain=i)
>>> d.gamsRepr()
'd(i)'
"""
representation = self.name
if self.domain:
representation += self._get_domain_str(self._domain_forwarding)
return representation
[docs]
def getDeclaration(self) -> str:
"""
Returns the GAMS declaration statement for this Parameter.
This string is used internally to declare the parameter in the GAMS execution environment.
Returns
-------
str
The GAMS declaration statement (e.g., 'Parameter p(i);').
Examples
--------
>>> import gamspy as gp
>>> m = gp.Container()
>>> i = gp.Set(m, "i", records=['i1','i2'])
>>> a = gp.Parameter(m, "a", [i], records=[['i1',1],['i2',2]])
>>> a.getDeclaration()
'Parameter a(i);'
"""
output = f"Parameter {self.gamsRepr()}"
if self.description:
output += ' "' + self.description + '"'
if self._records is None:
output += " / /"
if self.dimension == 0 and self._records is not None:
value = self._records["value"][0]
value = utils._map_special_values(value)
if isinstance(value, float) and np.isnan(value):
value = "Undf"
output += f" / {value} /"
output += ";"
return output
[docs]
def getAssignment(self) -> str:
"""
Returns the latest GAMS assignment statement for this Parameter.
This string represents the last assignment operation performed on the parameter
(e.g., 'p(i) = 5;').
Returns
-------
str
The GAMS assignment statement.
Raises
------
ValidationError
If the parameter has not been assigned yet.
Examples
--------
>>> import gamspy as gp
>>> m = gp.Container()
>>> i = gp.Set(m, "i", records=['i1','i2'])
>>> a = gp.Parameter(m, "a", [i], records=[['i1',1],['i2',2]])
>>> a[i] = a[i] * 5
>>> a.getAssignment()
'a(i) = a(i) * 5;'
"""
if self._assignment is None:
raise ValidationError("Parameter was not assigned!")
return self._assignment.getDeclaration()