Source code for gamspy._options

from __future__ import annotations

import io
import os
from pathlib import Path
import tempfile
from typing import TYPE_CHECKING, Literal, Optional

from gams import SymbolUpdateType, GamsOptions
from pydantic import BaseModel

from gamspy.exceptions import ValidationError

if TYPE_CHECKING:
    from gams import GamsWorkspace
    from gamspy._model import Problem

multi_solve_map = {"replace": 0, "merge": 1, "clear": 2}

# GAMSPy to GAMS Control mapping
option_map = {
    "cns": "cns",
    "dnlp": "dnlp",
    "emp": "emp",
    "lp": "lp",
    "mcp": "mcp",
    "minlp": "minlp",
    "mip": "mip",
    "miqcp": "miqcp",
    "mpec": "mpec",
    "nlp": "nlp",
    "qcp": "qcp",
    "rminlp": "rminlp",
    "rmip": "rmip",
    "rmiqcp": "rmiqcp",
    "rmpec": "rmpec",
    "allow_suffix_in_equation": "suffixalgebravars",
    "allow_suffix_in_limited_variables": "suffixdlvars",
    "basis_detection_threshold": "bratio",
    "compile_error_limit": "cerr",
    "domain_violation_limit": "domlim",
    "job_time_limit": "etlim",
    "job_heap_limit": "heaplimit",
    "hold_fixed_variables": "holdfixed",
    "integer_variable_upper_bound": "intvarup",
    "iteration_limit": "iterlim",
    "keep_temporary_files": "keep",
    "listing_file": "output",
    "log_file": "logfile",
    "variable_listing_limit": "limcol",
    "equation_listing_limit": "limrow",
    "node_limit": "nodlim",
    "absolute_optimality_gap": "optca",
    "relative_optimality_gap": "optcr",
    "profile": "profile",
    "profile_tolerance": "profiletol",
    "time_limit": "reslim",
    "savepoint": "savepoint",
    "seed": "seed",
    "report_solution": "solprint",
    "show_os_memory": "showosmemory",
    "solver_link_type": "solvelink",
    "merge_strategy": "solveopt",
    "step_summary": "stepsum",
    "suppress_compiler_listing": "suppress",
    "report_solver_status": "sysout",
    "threads": "threads",
    "write_listing_file": "writeoutput",
    "zero_rounding_threshold": "zerores",
    "report_underflow": "zeroresrep",
}


[docs] class Options(BaseModel): cns: Optional[str] = None dnlp: Optional[str] = None emp: Optional[str] = None lp: Optional[str] = None mcp: Optional[str] = None minlp: Optional[str] = None mip: Optional[str] = None miqcp: Optional[str] = None mpec: Optional[str] = None nlp: Optional[str] = None qcp: Optional[str] = None rminlp: Optional[str] = None rmip: Optional[str] = None rmiqcp: Optional[str] = None rmpec: Optional[str] = None allow_suffix_in_equation: Optional[bool] = None allow_suffix_in_limited_variables: Optional[bool] = None basis_detection_threshold: Optional[float] = None compile_error_limit: Optional[int] = None domain_violation_limit: Optional[int] = None job_time_limit: Optional[float] = None job_heap_limit: Optional[float] = None hold_fixed_variables: Optional[bool] = None integer_variable_upper_bound: Optional[int] = None iteration_limit: Optional[int] = None keep_temporary_files: Optional[int] = None listing_file: Optional[str] = None log_file: Optional[str] = None variable_listing_limit: Optional[int] = None equation_listing_limit: Optional[int] = None node_limit: Optional[int] = None absolute_optimality_gap: Optional[float] = None relative_optimality_gap: Optional[float] = None profile: Optional[int] = None profile_tolerance: Optional[float] = None time_limit: Optional[float] = None savepoint: Optional[Literal[0, 1, 2, 3, 4]] = None seed: Optional[int] = None report_solution: Optional[Literal[0, 1, 2]] = None show_os_memory: Optional[Literal[0, 1, 2]] = None solver_link_type: Optional[Literal[0, 1, 2, 3, 4, 5, 6, 7]] = None merge_strategy: Optional[Literal["replace", "merge", "clear"]] = None step_summary: Optional[bool] = None suppress_compiler_listing: Optional[bool] = None report_solver_status: Optional[bool] = None threads: Optional[int] = None write_listing_file: Optional[bool] = None zero_rounding_threshold: Optional[float] = None report_underflow: Optional[bool] = None def _get_gams_compatible_options(self, output: io.TextIOWrapper | None) -> dict: gamspy_options = self.model_dump(exclude_none=True) if "allow_suffix_in_equation" in gamspy_options: allows_suffix = gamspy_options["allow_suffix_in_equation"] gamspy_options["allow_suffix_in_equation"] = ( "on" if allows_suffix else "off" ) if "allow_suffix_in_limited_variables" in gamspy_options: allows_suffix = gamspy_options["allow_suffix_in_limited_variables"] gamspy_options["allow_suffix_in_limited_variables"] = ( "on" if allows_suffix else "off" ) if "merge_strategy" in gamspy_options: strategy = gamspy_options["merge_strategy"] gamspy_options["merge_strategy"] = multi_solve_map[strategy] if "listing_file" in gamspy_options: os.makedirs( Path(gamspy_options["listing_file"]).parent.absolute(), exist_ok=True, ) if not os.path.isabs(gamspy_options["listing_file"]): gamspy_options["listing_file"] = os.path.abspath( gamspy_options["listing_file"] ) if "log_file" in gamspy_options: os.makedirs( Path(gamspy_options["log_file"]).parent.absolute(), exist_ok=True, ) if not os.path.isabs(gamspy_options["log_file"]): gamspy_options["log_file"] = os.path.abspath( gamspy_options["log_file"] ) gams_options = dict() for key, value in gamspy_options.items(): value = int(value) if isinstance(value, bool) else value gams_options[option_map[key]] = value gams_options["previouswork"] = ( 1 # # In case GAMS version differs on backend ) gams_options["traceopt"] = 3 if self.log_file: if output is not None: gams_options["logoption"] = 4 else: gams_options["logoption"] = 2 else: if output is not None: gams_options["logoption"] = 3 else: gams_options["logoption"] = 0 return gams_options def _set_solver_options( self, working_directory: str, solver: str | None, problem: Problem, solver_options: dict | None, ): """Set the solver and the solver options""" if solver: self._solver = (str(problem), solver) if solver_options: if solver is None: raise ValidationError( "You need to provide a 'solver' to apply solver options." ) solver_file_name = os.path.join( working_directory, f"{solver.lower()}.123" ) with open(solver_file_name, "w", encoding="utf-8") as solver_file: for key, value in solver_options.items(): solver_file.write(f"{key} {value}\n") self._solver_options_file = "123" def _set_extra_options(self, options: dict) -> None: """Set extra options of the backend""" self._extra_options = options def export(self, pf_file: str, output: io.TextIOWrapper | None = None) -> None: all_options = dict() # Solver options if hasattr(self, "_solver"): problem_type, solver = self._solver all_options[problem_type] = solver if hasattr(self, "_solver_options_file"): all_options["optfile"] = self._solver_options_file # Extra options if hasattr(self, "_extra_options") and self._extra_options: all_options.update(**self._extra_options) # User options user_options = self._get_gams_compatible_options(output) all_options.update(**user_options) # Generate pf file with open(pf_file, "w") as file: file.write("\n".join([f"{key} = {value}" for key, value in all_options.items()])) def _get_gams_options(self, workspace: GamsWorkspace, output: io.TextIOWrapper | None = None) -> GamsOptions: temp_path = os.path.join(workspace.working_directory, "dummy.pf") self.export(temp_path, output) gams_options = GamsOptions(workspace, opt_file=temp_path) return gams_options
update_type_map = { "0": SymbolUpdateType.Zero, "base_case": SymbolUpdateType.BaseCase, "accumulate": SymbolUpdateType.Accumulate, "inherit": SymbolUpdateType._Inherit, }
[docs] class ModelInstanceOptions(BaseModel): solver: Optional[str] = None opt_file: int = -1 no_match_limit: int = 0 debug: bool = False update_type: Literal["0", "base_case", "accumulate", "inherit"] = ( "base_case" ) def items(self): dictionary = self.model_dump() dictionary["update_type"] = update_type_map[dictionary["update_type"]] return dictionary