Source code for gamspy.utils

#
# GAMS - General Algebraic Modeling System Python API
#
# Copyright (c) 2023 GAMS Development Corp. <support@gams.com>
# Copyright (c) 2023 GAMS Software GmbH <support@gams.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
from __future__ import annotations

import os
import platform
from collections.abc import Sequence
from typing import Iterable
from typing import TYPE_CHECKING

import gams.transfer as gt
from gams.core import gdx

import gamspy._symbols.implicits as implicits
from gamspy.exceptions import GamspyException
from gamspy.exceptions import ValidationError

if TYPE_CHECKING:
    from gamspy._symbols.implicits import ImplicitSet
    from gamspy import Alias, Set

SPECIAL_VALUE_MAP = {
    gt.SpecialValues.NA: "NA",
    gt.SpecialValues.EPS: "EPS",
    gt.SpecialValues.UNDEF: "UNDF",
    gt.SpecialValues.POSINF: "INF",
    gt.SpecialValues.NEGINF: "-INF",
}


[docs]def getInstalledSolvers() -> list[str]: """ Returns the list of installed solvers Returns ------- List[str] Raises ------ GamspyException In case gamspy_base is not installed. Examples -------- >>> import gamspy.utils as utils >>> installed_solvers = utils.getInstalledSolvers() """ try: import gamspy_base except ModuleNotFoundError as e: e.msg = "You must first install gamspy_base to use this functionality" raise e solver_names = [] capabilities_file = {"Windows": "gmscmpNT.txt", "rest": "gmscmpun.txt"} user_platform = "Windows" if platform.system() == "Windows" else "rest" with open( gamspy_base.directory + os.sep + capabilities_file[user_platform] ) as capabilities: for line in capabilities: if line == "DEFAULTS\n": break if line.isupper(): solver_names.append(line.split(" ")[0]) solver_names.remove("SCENSOLVER") return sorted(solver_names)
[docs]def getAvailableSolvers() -> list[str]: """ Returns all available solvers that can be installed. Returns ------- List[str] Raises ------ GamspyException In case gamspy_base is not installed. Examples -------- >>> import gamspy.utils as utils >>> available_solvers = utils.getAvailableSolvers() """ try: import gamspy_base except ModuleNotFoundError as e: e.msg = "You must first install gamspy_base to use this functionality" raise e return sorted(gamspy_base.available_solvers)
[docs]def checkAllSame(iterable1: Sequence, iterable2: Sequence) -> bool: """ Checks if all elements of a sequence are equal to the all elements of another sequence. Parameters ---------- iterable1 : Sequence of Symbols iterable2 : Sequence of Symbols Returns ------- bool Examples -------- >>> import gamspy as gp >>> m = gp.Container() >>> i = gp.Set(m, "i") >>> j = gp.Set(m, "j") >>> k = gp.Set(m, "k") >>> list1 = [i, j] >>> list2 = [i, j] >>> gp.utils.checkAllSame(list1, list2) True >>> list3 = [i, j, k] >>> gp.utils.checkAllSame(list1, list3) False """ if len(iterable1) != len(iterable2): return False all_same = True for elem1, elem2 in zip(iterable1, iterable2): if elem1 is not elem2: return False return all_same
[docs]def isin(symbol, sequence: Sequence) -> bool: """ Checks whether the given symbol in the sequence. Needed for symbol comparison since __eq__ magic is overloaded by the symbols. Parameters ---------- symbol : Symbol Symbol to check sequence : Sequence Sequence that holds a sequence of symbols Returns ------- bool Examples -------- >>> import gamspy as gp >>> m = gp.Container() >>> i = gp.Set(m, "i") >>> j = gp.Set(m, "j") >>> k = gp.Set(m, "k") >>> sets = [i, j] >>> gp.utils.isin(i, sets) True >>> gp.utils.isin(k, sets) False """ for item in sequence: if symbol is item: return True return False
def _get_gamspy_base_directory() -> str: """ Returns the gamspy_base directory. Returns ------- str System directory """ try: import gamspy_base except ModuleNotFoundError as e: e.msg = "You must first install gamspy_base to use this functionality" raise e gamspy_base_directory = gamspy_base.__path__[0] return gamspy_base_directory def _close_gdx_handle(handle): """ Closes the handle and unloads the gdx library. Parameters ---------- handle : gdxHandle """ gdx.gdxClose(handle) gdx.gdxFree(handle) gdx.gdxLibraryUnload() def _replace_equality_signs(string: str) -> str: string = string.replace("=l=", "<=") string = string.replace("=e=", "=") string = string.replace("=g=", ">=") return string def _open_gdx_file(system_directory: str, load_from: str): """ Opens the gdx file with given path Parameters ---------- system_directory : str load_from : str Returns ------- gdxHandle Raises ------ Exception Exception while creating the handle or setting the special values """ try: gdxHandle = gdx.new_gdxHandle_tp() rc = gdx.gdxCreateD(gdxHandle, system_directory, gdx.GMS_SSSIZE) assert rc[0], rc[1] except AssertionError: raise GamspyException("GAMSPy could not create gdx handle.") try: rc = gdx.gdxOpenRead(gdxHandle, load_from) assert rc[0] except AssertionError: raise GamspyException("GAMSPy could not open gdx file to read from.") return gdxHandle def _to_list(obj: Set | Alias | str | tuple | ImplicitSet) -> list: """ Converts the given object to a list Parameters ---------- obj : Set | Alias | str | tuple | list | Domain | Expression | ImplicitSet Object to be converted Returns ------- list """ if isinstance(obj, tuple): return list(obj) if not isinstance(obj, list): obj = [obj] return obj def _map_special_values(value: float): if value in SPECIAL_VALUE_MAP.keys(): return SPECIAL_VALUE_MAP[value] return value def _get_domain_str(domain: Iterable[Set | Alias | ImplicitSet | str]) -> str: """ Creates the string format of a given domain Parameters ---------- domain : Set | Alias | ImplicitSet | str Returns ------- str Raises ------ Exception Given domain must contain only sets, aliases or strings """ set_strs = [] for set in domain: if isinstance(set, (gt.Set, gt.Alias, implicits.ImplicitSet)): set_strs.append(set.gamsRepr()) elif isinstance(set, str): if set == "*": set_strs.append(set) else: set_strs.append('"' + set + '"') else: raise ValidationError( f"Domain type must be str, Set or Alias but found {type(set)}" ) return "(" + ",".join(set_strs) + ")" def _get_matching_paranthesis_indices(string: str) -> dict: """ Stack based paranthesis matcher. Parameters ---------- string : str Returns ------- dict Raises ------ Exception In case there are more closing paranthesis than opening parantheses Exception In case there are more opening paranthesis than closing parantheses """ stack = [] # stack of indices of opening parentheses matching_indices = {} for index, character in enumerate(string): if character == "(": stack.append(index) if character == ")": try: matching_indices[stack.pop()] = index except IndexError: raise AssertionError("Too many closing parentheses!") if stack: raise AssertionError("Too many opening parentheses!") return matching_indices