from __future__ import annotations
import os
import threading
import weakref
from typing import TYPE_CHECKING, Literal, cast
import pandas as pd
from gams.core import gdx
from gams.core.gdx import GMS_DT_ALIAS
import gamspy as gp
import gamspy._algebra.condition as condition
import gamspy._gdx as gdxio
import gamspy._validation as validation
from gamspy._symbols.base import BaseSymbol
from gamspy.exceptions import ValidationError
if TYPE_CHECKING:
from gamspy import Container
TEMP_ALIAS_NAME = "a" + gp.utils._get_unique_name()
TEMP_GDX_OUT_NAME = "_" + gp.utils._get_unique_name() + ".gdx"
[docs]
class UniverseAlias(BaseSymbol):
"""
Represents a UniverseAlias symbol in GAMS.
Parameters
----------
container : Container
name : str
Examples
--------
>>> import gamspy as gp
>>> m = gp.Container()
>>> universe = gp.UniverseAlias(m)
>>> universe.records
Empty DataFrame
Columns: [uni]
Index: []
>>> i = gp.Set(m, "i", records=['i1', 'i2'])
>>> universe.records
uni
0 i1
1 i2
"""
@classmethod
def _constructor_bypass(cls, container: Container, name: str) -> UniverseAlias:
# create new symbol object
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
## typing
obj._gams_type = GMS_DT_ALIAS
obj._gams_subtype = 0
## add to container
obj._container._data.update({name: obj})
# gamspy attributes
obj.where = condition.Condition(obj)
obj._latex_name = name.replace("_", r"\_")
obj.container._add_statement(obj)
return obj
def __new__(cls, container: Container | None = None, name: str = "universe"):
if container is not None and not isinstance(container, gp.Container):
raise TypeError(
f"Container must of type `Container` but found {type(container)}"
)
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 UniverseAlias object)"
)
def __init__(self, container: Container | None = None, name: str = "universe"):
if name is not None:
name = validation.validate_name(name)
else:
name = container._get_symbol_name(prefix="u")
self.name = name
if container is None:
try:
container = gp._ctx_managers[(os.getpid(), threading.get_native_id())]
except KeyError as e:
raise ValidationError("UniverseAlias requires a container.") from e
self._container = cast("Container", weakref.proxy(container))
# gtp attributes
self._gams_type = gdx.GMS_DT_ALIAS
self._gams_subtype = 0
self._container._data.update({name: self})
# gamspy attributes
self._latex_name = self.name.replace("_", r"\_")
self.where = condition.Condition(self)
self._container._add_statement(self)
self._container._synch_with_gams()
@property
def _should_unload_to_gams(self) -> bool:
return False
@_should_unload_to_gams.setter
def _should_unload_to_gams(self, value: bool) -> None: ...
@property
def _should_load_from_gams(self) -> bool:
return True
@_should_load_from_gams.setter
def _should_load_from_gams(self, value: bool) -> None: ...
def _serialize(self) -> dict:
return {}
def _deserialize(self, info: dict) -> None: ...
def __repr__(self) -> str:
return f"UniverseAlias(name='{self.name}')"
@property
def is_singleton(self) -> bool:
"""
Whether a symbol is a singleton set
Returns
-------
bool
Always False
"""
return False
@property
def alias_with(self) -> str:
"""
Returns aliased object
Returns
-------
str
Always "*"
"""
return "*"
@property
def domain_names(self) -> list[str]:
"""
Always ["*"] for universe alias
Returns
-------
list[str]
Always ["*"]
"""
return ["*"]
@property
def domain_labels(self) -> list[str]:
"""
Always ["uni"] for universe alias
Returns
-------
list[str]
Always ["uni"]
"""
return ["uni"]
@domain_labels.setter
def domain_labels(self, value) -> None: ...
@property
def domain(self) -> Literal["*"]:
"""
Always "*" for universe alias
Returns
-------
Literal
Always "*"
"""
return "*"
@property
def description(self) -> str:
"""
Always 'Aliased with *' for universe alias
Returns
-------
str
Always 'Aliased with *'
"""
return "Aliased with *"
@property
def dimension(self) -> int:
"""
Always 1 for universe alias
Returns
-------
int
Always 1
"""
return 1
[docs]
def toList(self) -> list[str] | None:
"""
Convenience method to return symbol records as a python list
Returns
-------
list[str] | None
A list of symbol records
Examples
--------
>>> import gamspy as gp
>>> m = gp.Container()
>>> i = gp.Set(m, "i", records=["seattle", "san-diego"])
>>> j = gp.Set(m, "j", records=["new-york", "chicago", "topeka"])
>>> ij = gp.UniverseAlias(m, "ij")
>>> print(ij.toList())
['seattle', 'san-diego', 'new-york', 'chicago', 'topeka']
"""
records = self.records
return records.set_index(records.columns[0]).index.to_list()
@property
def number_records(self) -> int:
"""
Number of symbol records
Returns
-------
int
Number of symbol records
"""
return len(self)
@property
def domain_type(self) -> str:
"""
Always none for universe alias
Returns
-------
str
Always 'none'
"""
return "none"
def _getUELs(self, ignore_unused: bool = False) -> list[str]:
"""
Gets UELs from the Container. Returns only UELs in the data if ignore_unused=True, otherwise return all UELs.
Parameters
----------
ignore_unused : bool, optional
Whether to get all UELs or only used ones, by default False
Returns
-------
list | None
A list of UELs if the symbol is valid, otherwise None.
"""
return self._container._getUELs(ignore_unused=ignore_unused)
@property
def summary(self) -> dict:
"""
Returns a dict of only the metadata
Returns
-------
dict
Outputs a dict of only the metadata
"""
return {
"name": self.name,
"description": self.description,
"alias_with": self.alias_with,
}
[docs]
def getSparsity(self) -> float:
"""
Get the sparsity of the symbol w.r.t the cardinality
Returns
-------
float
Always 0
"""
return 0.0
@property
def records(self) -> pd.DataFrame:
"""
Records of the UniverseAlias
Returns
-------
list[str]
Examples
--------
>>> import gamspy as gp
>>> import numpy as np
>>> m = gp.Container()
>>> i = gp.Set(m, name="i", records=["seattle", "san-diego"])
>>> uni = gp.UniverseAlias(m)
>>> uni.records
uni
0 seattle
1 san-diego
"""
global TEMP_ALIAS_NAME
global TEMP_GDX_OUT_NAME
temp_path = os.path.join(self._container.working_directory, TEMP_GDX_OUT_NAME)
self._container._add_statement(f"Alias (*, {TEMP_ALIAS_NAME});")
self._container._add_statement(
f"execute_unload '{temp_path}' {TEMP_ALIAS_NAME};"
)
self._container._synch_with_gams()
with gdxio.open_gdx(self._container.system_directory, temp_path) as handle:
uels: list[str] = self._container._gams2np.gdxGetUelList(handle)
return pd.DataFrame(uels[1:], columns=["uni"])
@records.setter
def records(self, value) -> None: ...
[docs]
def gamsRepr(self) -> str:
"""
Representation of the UniverseAlias in GAMS language.
Returns
-------
str
Examples
--------
>>> import gamspy as gp
>>> m = gp.Container()
>>> i = gp.UniverseAlias(m, name="universe")
>>> i.gamsRepr()
'universe'
"""
return self.name
[docs]
def latexRepr(self) -> str:
"""
Representation of the UniverseAlias in LaTeX.
Returns
-------
str
Examples
--------
>>> import gamspy as gp
>>> m = gp.Container()
>>> i = gp.UniverseAlias(m, name="universe")
>>> i.latexRepr()
'universe'
"""
return self._latex_name
[docs]
def getDeclaration(self) -> str:
"""
Declaration of the UniverseAlias in GAMS
Returns
-------
str
Examples
--------
>>> import gamspy as gp
>>> m = gp.Container()
>>> i = gp.UniverseAlias(m, name="universe")
>>> i.getDeclaration()
'Alias(universe,*);'
"""
return f"Alias({self.name},*);"