# ___________________________________________________________________________
#
# Pyomo: Python Optimization Modeling Objects
# Copyright (c) 2008-2024
# National Technology and Engineering Solutions of Sandia, LLC
# Under the terms of Contract DE-NA0003525 with National Technology and
# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain
# rights in this software.
# This software is distributed under the 3-clause BSD License.
# ___________________________________________________________________________
#
# Problem Writer for GAMS Format Files
#
from io import StringIO
from pyomo.common.gc_manager import PauseGC
import pyomo.core.expr as EXPR
from pyomo.core.expr.numvalue import (
value,
as_numeric,
native_types,
native_numeric_types,
nonpyomo_leaf_types,
)
from pyomo.core.expr.visitor import _ToStringVisitor
from pyomo.core.base import (
SymbolMap,
ShortNameLabeler,
NumericLabeler,
Constraint,
Objective,
Var,
minimize,
SortComponents,
)
from pyomo.core.base.component import ActiveComponent
from pyomo.core.kernel.base import ICategorizedObject
from pyomo.opt import ProblemFormat
from pyomo.opt.base import AbstractProblemWriter, WriterFactory
from pyomo.repn.util import valid_expr_ctypes_minlp, valid_active_ctypes_minlp, ftoa
import logging
logger = logging.getLogger('pyomo.core')
_legal_unary_functions = {
'ceil',
'floor',
'exp',
'log',
'log10',
'sqrt',
'sin',
'cos',
'tan',
'asin',
'acos',
'atan',
'sinh',
'cosh',
'tanh',
}
_arc_functions = {'acos', 'asin', 'atan'}
_dnlp_functions = {'ceil', 'floor', 'abs'}
_zero_one = {0, 1}
_plusMinusOne = {-1, 1}
def _handle_PowExpression(visitor, node, values):
# If the exponent is a positive integer, use the power() function.
# Otherwise, use the ** operator.
exponent = node.arg(1)
if exponent.__class__ in native_numeric_types and exponent == int(exponent):
return f"power({values[0]}, {values[1]})"
else:
return f"{values[0]} ** {values[1]}"
def _handle_UnaryFunctionExpression(visitor, node, values):
if node.name not in _legal_unary_functions:
raise RuntimeError(
"GAMS files cannot represent the unary function %s" % (node.name,)
)
if node.name in _dnlp_functions:
visitor.is_discontinuous = True
if node.name in _arc_functions:
return f"arc{node.name[1:]}({values[0]})"
else:
return node._to_string(values, False, visitor.smap)
def _handle_AbsExpression(visitor, node, values):
visitor.is_discontinuous = True
return node._to_string(values, False, visitor.smap)
#
# A visitor pattern that creates a string for an expression
# that is compatible with the GAMS syntax.
#
[docs]
class ToGamsVisitor(_ToStringVisitor):
_expression_handlers = {
EXPR.PowExpression: _handle_PowExpression,
EXPR.UnaryFunctionExpression: _handle_UnaryFunctionExpression,
EXPR.AbsExpression: _handle_AbsExpression,
}
[docs]
def __init__(self, smap, treechecker, output_fixed_variables=False):
super(ToGamsVisitor, self).__init__(False, smap)
self.treechecker = treechecker
self.is_discontinuous = False
self.output_fixed_variables = output_fixed_variables
[docs]
def visiting_potential_leaf(self, node):
"""
Visiting a potential leaf.
Return True if the node is not expanded.
"""
if node.__class__ in native_types:
try:
return True, ftoa(node, True)
except TypeError:
return True, repr(node)
if node.is_expression_type():
# Special handling if NPV and semi-NPV types:
if not node.is_potentially_variable():
return True, ftoa(node(), True)
if node.__class__ is EXPR.MonomialTermExpression:
return True, self._monomial_to_string(node)
if node.__class__ is EXPR.LinearExpression:
return True, self._linear_to_string(node)
# we will descend into this, so type checking will happen later
if node.is_component_type():
self.treechecker(node)
return False, None
if node.is_component_type():
if node.ctype not in valid_expr_ctypes_minlp:
# Make sure all components in active constraints
# are basic ctypes we know how to deal with.
raise RuntimeError(
"Unallowable component '%s' of type %s found in an active "
"constraint or objective.\nThe GAMS writer cannot export "
"expressions with this component type."
% (node.name, node.ctype.__name__)
)
if node.ctype is not Var:
# For these, make sure it's on the right model. We can check
# Vars later since they don't disappear from the expressions
self.treechecker(node)
if node.is_fixed() and not (
self.output_fixed_variables and node.is_potentially_variable()
):
return True, ftoa(node(), True)
else:
assert node.is_variable_type()
return True, self.smap.getSymbol(node)
def _monomial_to_string(self, node):
const, var = node.args
if const.__class__ not in native_types:
const = value(const)
if var.is_fixed() and not self.output_fixed_variables:
return ftoa(const * var.value, True)
# Special handling: ftoa is slow, so bypass _to_string when this
# is a trivial term
if not const:
return '0'
if const in _plusMinusOne:
if const < 0:
return '-' + self.smap.getSymbol(var)
else:
return self.smap.getSymbol(var)
return ftoa(const, True) + '*' + self.smap.getSymbol(var)
def _linear_to_string(self, node):
values = [
(
self._monomial_to_string(arg)
if arg.__class__ is EXPR.MonomialTermExpression
else (
ftoa(arg, True)
if arg.__class__ in native_numeric_types
else (
self.smap.getSymbol(arg)
if arg.is_variable_type()
and (not arg.fixed or self.output_fixed_variables)
else ftoa(value(arg), True)
)
)
)
for arg in node.args
]
return node._to_string(values, False, self.smap)
[docs]
def expression_to_string(expr, treechecker, smap=None, output_fixed_variables=False):
visitor = ToGamsVisitor(smap, treechecker, output_fixed_variables)
expr_str = visitor.dfs_postorder_stack(expr)
return expr_str, visitor.is_discontinuous
[docs]
class Categorizer(object):
"""Class for representing categorized variables.
Given a list of variable names and a symbol map, categorizes the variable
names into the categories: binary, ints, positive and reals.
"""
[docs]
def __init__(self, var_list, symbol_map):
self.binary = []
self.ints = []
self.positive = []
self.reals = []
self.fixed = []
# categorize variables
for var in var_list:
v = symbol_map.getObject(var)
if v.is_fixed():
self.fixed.append(var)
elif v.is_continuous():
if v.lb == 0:
self.positive.append(var)
else:
self.reals.append(var)
elif v.is_integer():
if all(bnd in _zero_one for bnd in v.bounds):
self.binary.append(var)
else:
self.ints.append(var)
else:
raise RuntimeError(
"Cannot output variable to GAMS: effective variable "
"domain is not in {Reals, Integers, Binary}"
)
def __iter__(self):
"""Iterate over all variables.
Yield a tuple containing the variables category and its name.
"""
for category in ['binary', 'ints', 'positive', 'reals']:
var_list = getattr(self, category)
for var_name in var_list:
yield category, var_name
[docs]
class StorageTreeChecker(object):
[docs]
def __init__(self, model):
# blocks are hashable so we can use a normal set
self.tree = {model}
self.model = model
# add everything above the model
pb = self.parent_block(model)
while pb is not None:
self.tree.add(pb)
pb = self.parent_block(pb)
def __call__(self, comp, exception_flag=True):
if comp is self.model:
return True
# walk up tree until there are no more parents
seen = set()
pb = self.parent_block(comp)
while pb is not None:
if pb in self.tree:
self.tree.update(seen)
return True
seen.add(pb)
pb = self.parent_block(pb)
if exception_flag:
self.raise_error(comp)
else:
return False
def parent_block(self, comp):
if isinstance(comp, ICategorizedObject):
parent = comp.parent
while (parent is not None) and (not parent._is_heterogeneous_container):
parent = parent.parent
return parent
else:
return comp.parent_block()
def raise_error(self, comp):
raise RuntimeError(
"GAMS writer: found component '%s' not on same model tree.\n"
"All components must have the same parent model." % comp.name
)
[docs]
def split_long_line(line):
"""
GAMS has an 80,000 character limit for lines, so split as many
times as needed so as to not have illegal lines.
"""
new_lines = ''
while len(line) > 80000:
i = 80000
while line[i] != ' ':
# Walk backwards to find closest space,
# where it is safe to split to a new line
if i < 0:
raise RuntimeError("Found an 80,000+ character string with no spaces")
i -= 1
new_lines += line[:i] + '\n'
# the space will be the first character in the next line,
# so that the line doesn't start with the comment character '*'
line = line[i:]
new_lines += line
return new_lines
[docs]
class GAMSSymbolMap(SymbolMap):
[docs]
def __init__(self, var_labeler, var_list):
super().__init__(self.var_label)
self.var_labeler = var_labeler
self.var_list = var_list
def var_label(self, obj):
# if obj.is_fixed():
# return str(value(obj))
return self.getSymbol(obj, self.var_recorder)
def var_recorder(self, obj):
ans = self.var_labeler(obj)
try:
if obj.is_variable_type():
self.var_list.append(ans)
except:
pass
return ans
[docs]
@WriterFactory.register('gams', 'Generate the corresponding GAMS file')
class ProblemWriter_gams(AbstractProblemWriter):
[docs]
def __init__(self):
AbstractProblemWriter.__init__(self, ProblemFormat.gams)
def __call__(self, model, output_filename, solver_capability, io_options):
"""
Write a model in the GAMS modeling language format.
Keyword Arguments
-----------------
output_filename: str
Name of file to write GAMS model to. Optionally pass a file-like
stream and the model will be written to that instead.
io_options: dict
- warmstart=True
Warmstart by initializing model's variables to their values.
- symbolic_solver_labels=False
Use full Pyomo component names rather than
shortened symbols (slower, but useful for debugging).
- labeler=None
Custom labeler. Incompatible with symbolic_solver_labels.
- solver=None
If None, GAMS will use default solver for model type.
- mtype=None
Model type. If None, will chose from lp, nlp, mip, and minlp.
- add_options=None
List of additional lines to write directly
into model file before the solve statement.
For model attributes, <model name> is GAMS_MODEL.
- skip_trivial_constraints=False
Skip writing constraints whose body section is fixed.
- output_fixed_variables=False
If True, output fixed variables as variables; otherwise,
output numeric value.
- file_determinism=1
| How much effort do we want to put into ensuring the
| GAMS file is written deterministically for a Pyomo model:
| 0 : None
| 1 : sort keys of indexed components (default)
| 2 : sort keys AND sort names (over declaration order)
- put_results=None
Filename for optionally writing solution values and
marginals. If put_results_format is 'gdx', then GAMS
will write solution values and marginals to
GAMS_MODEL_p.gdx and solver statuses to
{put_results}_s.gdx. If put_results_format is 'dat',
then solution values and marginals are written to
(put_results).dat, and solver statuses to (put_results +
'stat').dat.
- put_results_format='gdx'
Format used for put_results, one of 'gdx', 'dat'.
"""
# Make sure not to modify the user's dictionary,
# they may be reusing it outside of this call
io_options = dict(io_options)
# Use full Pyomo component names rather than
# shortened symbols (slower, but useful for debugging).
symbolic_solver_labels = io_options.pop("symbolic_solver_labels", False)
# Custom labeler option. Incompatible with symbolic_solver_labels.
labeler = io_options.pop("labeler", None)
# If None, GAMS will use default solver for model type.
solver = io_options.pop("solver", None)
# If None, will chose from lp, nlp, mip, and minlp.
mtype = io_options.pop("mtype", None)
# Improved GAMS calling options
solprint = io_options.pop("solprint", "off")
limrow = io_options.pop("limrow", 0)
limcol = io_options.pop("limcol", 0)
solvelink = io_options.pop("solvelink", 5)
# Lines to add before solve statement.
add_options = io_options.pop("add_options", None)
# Skip writing constraints whose body section is
# fixed (i.e., no variables)
skip_trivial_constraints = io_options.pop("skip_trivial_constraints", False)
# Output fixed variables as variables
output_fixed_variables = io_options.pop("output_fixed_variables", False)
# How much effort do we want to put into ensuring the
# GAMS file is written deterministically for a Pyomo model:
# 0 : None
# 1 : sort keys of indexed components (default)
# 2 : sort keys AND sort names (over declaration order)
file_determinism = io_options.pop("file_determinism", 1)
sorter_map = {
0: SortComponents.unsorted,
1: SortComponents.deterministic,
2: SortComponents.sortBoth,
}
sort = sorter_map[file_determinism]
# Warmstart by initializing model's variables to their values.
warmstart = io_options.pop("warmstart", True)
# Filename for optionally writing solution values and marginals
# Set to True by GAMSSolver
put_results = io_options.pop("put_results", None)
put_results_format = io_options.pop("put_results_format", 'gdx')
assert put_results_format in ('gdx', 'dat')
if len(io_options):
raise ValueError(
"GAMS writer passed unrecognized io_options:\n\t"
+ "\n\t".join("%s = %s" % (k, v) for k, v in io_options.items())
)
if solver is not None and solver.upper() not in valid_solvers:
raise ValueError("GAMS writer passed unrecognized solver: %s" % solver)
if mtype is not None:
valid_mtypes = set(
[
'lp',
'qcp',
'nlp',
'dnlp',
'rmip',
'mip',
'rmiqcp',
'rminlp',
'miqcp',
'minlp',
'rmpec',
'mpec',
'mcp',
'cns',
'emp',
]
)
if mtype.lower() not in valid_mtypes:
raise ValueError(
"GAMS writer passed unrecognized model type: %s" % mtype
)
if (
solver is not None
and mtype.upper() not in valid_solvers[solver.upper()]
):
raise ValueError(
"GAMS writer passed solver (%s) "
"unsuitable for given model type (%s)" % (solver, mtype)
)
if output_filename is None:
output_filename = model.name + ".gms"
if symbolic_solver_labels and (labeler is not None):
raise ValueError(
"GAMS writer: Using both the "
"'symbolic_solver_labels' and 'labeler' "
"I/O options is forbidden"
)
if symbolic_solver_labels:
# Note that the Var and Constraint labelers must use the
# same labeler, so that we can correctly detect name
# collisions (which can arise when we truncate the labels to
# the max allowable length. GAMS requires all identifiers
# to start with a letter. We will (randomly) choose "s_"
# (for 'shortened')
var_labeler = con_labeler = ShortNameLabeler(
60,
prefix='s_',
suffix='_',
caseInsensitive=True,
legalRegex='^[a-zA-Z]',
)
elif labeler is None:
var_labeler = NumericLabeler('x')
con_labeler = NumericLabeler('c')
else:
var_labeler = con_labeler = labeler
var_list = []
symbolMap = GAMSSymbolMap(var_labeler, var_list)
# when sorting, there are a non-trivial number of
# temporary objects created. these all yield
# non-circular references, so disable GC - the
# overhead is non-trivial, and because references
# are non-circular, everything will be collected
# immediately anyway.
with PauseGC() as pgc:
try:
if isinstance(output_filename, str):
output_file = open(output_filename, "w")
else:
# Support passing of stream such as a StringIO
# on which to write the model file
output_file = output_filename
self._write_model(
model=model,
output_file=output_file,
solver_capability=solver_capability,
var_list=var_list,
var_label=symbolMap.var_label,
symbolMap=symbolMap,
con_labeler=con_labeler,
sort=sort,
skip_trivial_constraints=skip_trivial_constraints,
output_fixed_variables=output_fixed_variables,
warmstart=warmstart,
solver=solver,
mtype=mtype,
solprint=solprint,
limrow=limrow,
limcol=limcol,
solvelink=solvelink,
add_options=add_options,
put_results=put_results,
put_results_format=put_results_format,
)
finally:
if isinstance(output_filename, str):
output_file.close()
return output_filename, symbolMap
def _write_model(
self,
model,
output_file,
solver_capability,
var_list,
var_label,
symbolMap,
con_labeler,
sort,
skip_trivial_constraints,
output_fixed_variables,
warmstart,
solver,
mtype,
solprint,
limrow,
limcol,
solvelink,
add_options,
put_results,
put_results_format,
):
constraint_names = []
ConstraintIO = StringIO()
linear = True
linear_degree = set([0, 1])
dnlp = False
# Make sure there are no strange ActiveComponents. The expression
# walker will handle strange things in constraints later.
model_ctypes = model.collect_ctypes(active=True)
invalids = set()
for t in model_ctypes - valid_active_ctypes_minlp:
if issubclass(t, ActiveComponent):
invalids.add(t)
if len(invalids):
invalids = [t.__name__ for t in invalids]
raise RuntimeError(
"Unallowable active component(s) %s.\nThe GAMS writer cannot "
"export models with this component type." % ", ".join(invalids)
)
tc = StorageTreeChecker(model)
# Walk through the model and generate the constraint definition
# for all active constraints. Any Vars / Expressions that are
# encountered will be added to the var_list due to the labeler
# defined above.
for con in model.component_data_objects(Constraint, active=True, sort=sort):
lb, body, ub = con.to_bounded_expression(True)
if lb is None and ub is None:
assert not con.equality
continue # non-binding, so skip
con_body = as_numeric(body)
if skip_trivial_constraints and con_body.is_fixed():
continue
if linear:
if con_body.polynomial_degree() not in linear_degree:
linear = False
cName = symbolMap.getSymbol(con, con_labeler)
con_body_str, con_discontinuous = expression_to_string(
con_body,
tc,
smap=symbolMap,
output_fixed_variables=output_fixed_variables,
)
dnlp |= con_discontinuous
if con.equality:
constraint_names.append('%s' % cName)
ConstraintIO.write(
'%s.. %s =e= %s ;\n'
% (constraint_names[-1], con_body_str, ftoa(ub, False))
)
else:
if lb is not None:
constraint_names.append('%s_lo' % cName)
ConstraintIO.write(
'%s.. %s =l= %s ;\n'
% (constraint_names[-1], ftoa(lb, False), con_body_str)
)
if ub is not None:
constraint_names.append('%s_hi' % cName)
ConstraintIO.write(
'%s.. %s =l= %s ;\n'
% (constraint_names[-1], con_body_str, ftoa(ub, False))
)
obj = list(model.component_data_objects(Objective, active=True, sort=sort))
if len(obj) != 1:
raise RuntimeError(
"GAMS writer requires exactly one active objective (found %s)"
% (len(obj))
)
obj = obj[0]
if linear:
if obj.polynomial_degree() not in linear_degree:
linear = False
obj_expr_str, obj_discontinuous = expression_to_string(
obj.expr, tc, smap=symbolMap, output_fixed_variables=output_fixed_variables
)
dnlp |= obj_discontinuous
oName = symbolMap.getSymbol(obj, con_labeler)
constraint_names.append(oName)
ConstraintIO.write('%s.. GAMS_OBJECTIVE =e= %s ;\n' % (oName, obj_expr_str))
# Categorize the variables that we found
categorized_vars = Categorizer(var_list, symbolMap)
# Write the GAMS model
output_file.write("$offlisting\n")
# $offdigit ignores extra precise digits instead of erroring
output_file.write("$offdigit\n\n")
output_file.write("EQUATIONS\n\t")
output_file.write("\n\t".join(constraint_names))
if categorized_vars.binary:
output_file.write(";\n\nBINARY VARIABLES\n\t")
output_file.write("\n\t".join(categorized_vars.binary))
if categorized_vars.ints:
output_file.write(";\n\nINTEGER VARIABLES")
output_file.write("\n\t")
output_file.write("\n\t".join(categorized_vars.ints))
if categorized_vars.positive:
output_file.write(";\n\nPOSITIVE VARIABLES\n\t")
output_file.write("\n\t".join(categorized_vars.positive))
output_file.write(";\n\nVARIABLES\n\tGAMS_OBJECTIVE\n\t")
output_file.write("\n\t".join(categorized_vars.reals + categorized_vars.fixed))
output_file.write(";\n\n")
for var in categorized_vars.fixed:
output_file.write(
"%s.fx = %s;\n" % (var, ftoa(value(symbolMap.getObject(var)), False))
)
output_file.write("\n")
for line in ConstraintIO.getvalue().splitlines():
if len(line) > 80000:
line = split_long_line(line)
output_file.write(line + "\n")
output_file.write("\n")
warn_int_bounds = False
for category, var_name in categorized_vars:
var = symbolMap.getObject(var_name)
tc(var)
lb, ub = var.bounds
if category == 'positive':
if ub is not None:
output_file.write("%s.up = %s;\n" % (var_name, ftoa(ub, False)))
elif category == 'ints':
if lb is None:
warn_int_bounds = True
# GAMS doesn't allow -INF lower bound for ints
logger.warning(
"Lower bound for integer variable %s set "
"to -1.0E+100." % var.name
)
output_file.write("%s.lo = -1.0E+100;\n" % (var_name))
elif lb != 0:
output_file.write("%s.lo = %s;\n" % (var_name, ftoa(lb, False)))
if ub is None:
warn_int_bounds = True
# GAMS has an option value called IntVarUp that is the
# default upper integer bound, which it applies if the
# integer's upper bound is INF. This option maxes out at
# 2147483647, so we can go higher by setting the bound.
logger.warning(
"Upper bound for integer variable %s set "
"to +1.0E+100." % var.name
)
output_file.write("%s.up = +1.0E+100;\n" % (var_name))
else:
output_file.write("%s.up = %s;\n" % (var_name, ftoa(ub, False)))
elif category == 'binary':
if lb != 0:
output_file.write("%s.lo = %s;\n" % (var_name, ftoa(lb, False)))
if ub != 1:
output_file.write("%s.up = %s;\n" % (var_name, ftoa(ub, False)))
elif category == 'reals':
if lb is not None:
output_file.write("%s.lo = %s;\n" % (var_name, ftoa(lb, False)))
if ub is not None:
output_file.write("%s.up = %s;\n" % (var_name, ftoa(ub, False)))
else:
raise KeyError('Category %s not supported' % category)
if warmstart and var.value is not None:
output_file.write("%s.l = %s;\n" % (var_name, ftoa(var.value, False)))
if warn_int_bounds:
logger.warning(
"GAMS requires finite bounds for integer variables. 1.0E100 "
"is as extreme as GAMS will define, and should be enough to "
"appear unbounded. If the solver cannot handle this bound, "
"explicitly set a smaller bound on the pyomo model, or try a "
"different GAMS solver."
)
model_name = "GAMS_MODEL"
output_file.write("\nMODEL %s /all/ ;\n" % model_name)
if mtype is None:
mtype = ('lp', 'nlp', 'mip', 'minlp')[
(0 if linear else 1)
+ (2 if (categorized_vars.binary or categorized_vars.ints) else 0)
]
if mtype == 'nlp' and dnlp:
mtype = 'dnlp'
if solver is not None:
if mtype.upper() not in valid_solvers[solver.upper()]:
raise ValueError(
"GAMS writer passed solver (%s) "
"unsuitable for model type (%s)" % (solver, mtype)
)
output_file.write("option %s=%s;\n" % (mtype, solver))
output_file.write("option solprint=%s;\n" % solprint)
output_file.write("option limrow=%d;\n" % limrow)
output_file.write("option limcol=%d;\n" % limcol)
output_file.write("option solvelink=%d;\n" % solvelink)
if put_results is not None and put_results_format == 'gdx':
output_file.write("option savepoint=1;\n")
if add_options is not None:
output_file.write("\n* START USER ADDITIONAL OPTIONS\n")
for line in add_options:
output_file.write('\n' + line)
output_file.write("\n\n* END USER ADDITIONAL OPTIONS\n\n")
output_file.write(
"SOLVE %s USING %s %simizing GAMS_OBJECTIVE;\n\n"
% (model_name, mtype, 'min' if obj.sense == minimize else 'max')
)
# Set variables to store certain statuses and attributes
stat_vars = [
'MODELSTAT',
'SOLVESTAT',
'OBJEST',
'OBJVAL',
'NUMVAR',
'NUMEQU',
'NUMDVAR',
'NUMNZ',
'ETSOLVE',
]
output_file.write(
"Scalars MODELSTAT 'model status', SOLVESTAT 'solve status';\n"
)
output_file.write("MODELSTAT = %s.modelstat;\n" % model_name)
output_file.write("SOLVESTAT = %s.solvestat;\n\n" % model_name)
output_file.write("Scalar OBJEST 'best objective', OBJVAL 'objective value';\n")
output_file.write("OBJEST = %s.objest;\n" % model_name)
output_file.write("OBJVAL = %s.objval;\n\n" % model_name)
output_file.write("Scalar NUMVAR 'number of variables';\n")
output_file.write("NUMVAR = %s.numvar\n\n" % model_name)
output_file.write("Scalar NUMEQU 'number of equations';\n")
output_file.write("NUMEQU = %s.numequ\n\n" % model_name)
output_file.write("Scalar NUMDVAR 'number of discrete variables';\n")
output_file.write("NUMDVAR = %s.numdvar\n\n" % model_name)
output_file.write("Scalar NUMNZ 'number of nonzeros';\n")
output_file.write("NUMNZ = %s.numnz\n\n" % model_name)
output_file.write("Scalar ETSOLVE 'time to execute solve statement';\n")
output_file.write("ETSOLVE = %s.etsolve\n\n" % model_name)
if put_results is not None:
if put_results_format == 'gdx':
output_file.write("\nexecute_unload '%s_s.gdx'" % put_results)
for stat in stat_vars:
output_file.write(", %s" % stat)
output_file.write(";\n")
else:
results = put_results + '.dat'
output_file.write("\nfile results /'%s'/;" % results)
output_file.write("\nresults.nd=15;")
output_file.write("\nresults.nw=21;")
output_file.write("\nput results;")
output_file.write("\nput 'SYMBOL : LEVEL : MARGINAL' /;")
for var in var_list:
output_file.write("\nput %s ' ' %s.l ' ' %s.m /;" % (var, var, var))
for con in constraint_names:
output_file.write("\nput %s ' ' %s.l ' ' %s.m /;" % (con, con, con))
output_file.write(
"\nput GAMS_OBJECTIVE ' ' GAMS_OBJECTIVE.l "
"' ' GAMS_OBJECTIVE.m;\n"
)
statresults = put_results + 'stat.dat'
output_file.write("\nfile statresults /'%s'/;" % statresults)
output_file.write("\nstatresults.nd=15;")
output_file.write("\nstatresults.nw=21;")
output_file.write("\nput statresults;")
output_file.write("\nput 'SYMBOL : VALUE' /;")
for stat in stat_vars:
output_file.write("\nput '%s' ' ' %s /;\n" % (stat, stat))
valid_solvers = {
'ALPHAECP': {'MINLP', 'MIQCP'},
'AMPL': {
'LP',
'MIP',
'RMIP',
'NLP',
'MCP',
'MPEC',
'RMPEC',
'CNS',
'DNLP',
'RMINLP',
'MINLP',
},
'ANTIGONE': {'NLP', 'CNS', 'DNLP', 'RMINLP', 'MINLP', 'QCP', 'MIQCP', 'RMIQCP'},
'BARON': {
'LP',
'MIP',
'RMIP',
'NLP',
'CNS',
'DNLP',
'RMINLP',
'MINLP',
'QCP',
'MIQCP',
'RMIQCP',
},
'BDMLP': {'LP', 'MIP', 'RMIP'},
'BDMLPD': {'LP', 'RMIP'},
'BENCH': {
'LP',
'MIP',
'RMIP',
'NLP',
'MCP',
'MPEC',
'RMPEC',
'CNS',
'DNLP',
'RMINLP',
'MINLP',
'QCP',
'MIQCP',
'RMIQCP',
},
'BONMIN': {'MINLP', 'MIQCP'},
'BONMINH': {'MINLP', 'MIQCP'},
'CBC': {'LP', 'MIP', 'RMIP'},
'COINBONMIN': {'MINLP', 'MIQCP'},
'COINCBC': {'LP', 'MIP', 'RMIP'},
'COINCOUENNE': {'NLP', 'CNS', 'DNLP', 'RMINLP', 'MINLP', 'QCP', 'MIQCP', 'RMIQCP'},
'COINIPOPT': {'LP', 'RMIP', 'NLP', 'CNS', 'DNLP', 'RMINLP', 'QCP', 'RMIQCP'},
'COINOS': {
'LP',
'MIP',
'RMIP',
'NLP',
'CNS',
'DNLP',
'RMINLP',
'MINLP',
'QCP',
'MIQCP',
'RMIQCP',
},
'COINSCIP': {
'MIP',
'NLP',
'CNS',
'DNLP',
'RMINLP',
'MINLP',
'QCP',
'MIQCP',
'RMIQCP',
},
'CONOPT': {'LP', 'RMIP', 'NLP', 'CNS', 'DNLP', 'RMINLP', 'QCP', 'RMIQCP'},
'CONOPT3': {'LP', 'RMIP', 'NLP', 'CNS', 'DNLP', 'RMINLP', 'QCP', 'RMIQCP'},
'CONOPT4': {'LP', 'RMIP', 'NLP', 'CNS', 'DNLP', 'RMINLP', 'QCP', 'RMIQCP'},
'CONOPTD': {'LP', 'RMIP', 'NLP', 'CNS', 'DNLP', 'RMINLP', 'QCP', 'RMIQCP'},
'CONVERT': {
'LP',
'MIP',
'RMIP',
'NLP',
'MCP',
'MPEC',
'RMPEC',
'CNS',
'DNLP',
'RMINLP',
'MINLP',
'QCP',
'MIQCP',
'RMIQCP',
},
'CONVERTD': {
'LP',
'MIP',
'RMIP',
'NLP',
'MCP',
'MPEC',
'RMPEC',
'CNS',
'DNLP',
'RMINLP',
'MINLP',
'QCP',
'MIQCP',
'RMIQCP',
'EMP',
},
'COUENNE': {'NLP', 'CNS', 'DNLP', 'RMINLP', 'MINLP', 'QCP', 'MIQCP', 'RMIQCP'},
'CPLEX': {'LP', 'MIP', 'RMIP', 'QCP', 'MIQCP', 'RMIQCP'},
'CPLEXD': {'LP', 'MIP', 'RMIP', 'QCP', 'MIQCP', 'RMIQCP'},
'CPOPTIMIZER': {'MIP', 'MINLP', 'MIQCP'},
'DE': {'EMP'},
'DECIS': {'EMP'},
'DECISC': {'LP'},
'DECISM': {'LP'},
'DICOPT': {'MINLP', 'MIQCP'},
'DICOPTD': {'MINLP', 'MIQCP'},
'EXAMINER': {
'LP',
'MIP',
'RMIP',
'NLP',
'MCP',
'MPEC',
'RMPEC',
'DNLP',
'RMINLP',
'MINLP',
'QCP',
'MIQCP',
'RMIQCP',
},
'EXAMINER2': {
'LP',
'MIP',
'RMIP',
'NLP',
'MCP',
'DNLP',
'RMINLP',
'MINLP',
'QCP',
'MIQCP',
'RMIQCP',
},
'GAMSCHK': {
'LP',
'MIP',
'RMIP',
'NLP',
'MCP',
'DNLP',
'RMINLP',
'MINLP',
'QCP',
'MIQCP',
'RMIQCP',
},
'GLOMIQO': {'QCP', 'MIQCP', 'RMIQCP'},
'GUROBI': {'LP', 'MIP', 'RMIP', 'QCP', 'MIQCP', 'RMIQCP'},
'GUSS': {'LP', 'MIP', 'NLP', 'MCP', 'CNS', 'DNLP', 'MINLP', 'QCP', 'MIQCP'},
'IPOPT': {'LP', 'RMIP', 'NLP', 'CNS', 'DNLP', 'RMINLP', 'QCP', 'RMIQCP'},
'IPOPTH': {'LP', 'RMIP', 'NLP', 'CNS', 'DNLP', 'RMINLP', 'QCP', 'RMIQCP'},
'JAMS': {'EMP'},
'KESTREL': {
'LP',
'MIP',
'RMIP',
'NLP',
'MCP',
'MPEC',
'RMPEC',
'CNS',
'DNLP',
'RMINLP',
'MINLP',
'QCP',
'MIQCP',
'RMIQCP',
'EMP',
},
'KNITRO': {
'LP',
'RMIP',
'NLP',
'MPEC',
'RMPEC',
'CNS',
'DNLP',
'RMINLP',
'MINLP',
'QCP',
'MIQCP',
'RMIQCP',
},
'LGO': {'LP', 'RMIP', 'NLP', 'DNLP', 'RMINLP', 'QCP', 'RMIQCP'},
'LGOD': {'LP', 'RMIP', 'NLP', 'DNLP', 'RMINLP', 'QCP', 'RMIQCP'},
'LINDO': {
'LP',
'MIP',
'RMIP',
'NLP',
'DNLP',
'RMINLP',
'MINLP',
'QCP',
'MIQCP',
'RMIQCP',
'EMP',
},
'LINDOGLOBAL': {
'LP',
'MIP',
'RMIP',
'NLP',
'DNLP',
'RMINLP',
'MINLP',
'QCP',
'MIQCP',
'RMIQCP',
},
'LINGO': {'LP', 'MIP', 'RMIP', 'NLP', 'DNLP', 'RMINLP', 'MINLP'},
'LOCALSOLVER': {
'MIP',
'NLP',
'CNS',
'DNLP',
'RMINLP',
'MINLP',
'QCP',
'MIQCP',
'RMIQCP',
},
'LOGMIP': {'EMP'},
'LS': {'LP', 'RMIP'},
'MILES': {'MCP'},
'MILESE': {'MCP'},
'MINOS': {'LP', 'RMIP', 'NLP', 'CNS', 'DNLP', 'RMINLP', 'QCP', 'RMIQCP'},
'MINOS5': {'LP', 'RMIP', 'NLP', 'CNS', 'DNLP', 'RMINLP', 'QCP', 'RMIQCP'},
'MINOS55': {'LP', 'RMIP', 'NLP', 'CNS', 'DNLP', 'RMINLP', 'QCP', 'RMIQCP'},
'MOSEK': {'LP', 'MIP', 'RMIP', 'NLP', 'DNLP', 'RMINLP', 'QCP', 'MIQCP', 'RMIQCP'},
'MPECDUMP': {
'LP',
'MIP',
'RMIP',
'NLP',
'MCP',
'MPEC',
'RMPEC',
'CNS',
'DNLP',
'RMINLP',
'MINLP',
},
'MPSGE': {},
'MSNLP': {'NLP', 'DNLP', 'RMINLP', 'QCP', 'RMIQCP'},
'NLPEC': {'MCP', 'MPEC', 'RMPEC'},
'OQNLP': {'NLP', 'DNLP', 'MINLP', 'QCP', 'MIQCP'},
'OS': {
'LP',
'MIP',
'RMIP',
'NLP',
'CNS',
'DNLP',
'RMINLP',
'MINLP',
'QCP',
'MIQCP',
'RMIQCP',
},
'OSICPLEX': {'LP', 'MIP', 'RMIP'},
'OSIGUROBI': {'LP', 'MIP', 'RMIP'},
'OSIMOSEK': {'LP', 'MIP', 'RMIP'},
'OSISOPLEX': {'LP', 'RMIP'},
'OSIXPRESS': {'LP', 'MIP', 'RMIP'},
'PATH': {'MCP', 'CNS'},
'PATHC': {'MCP', 'CNS'},
'PATHNLP': {'LP', 'RMIP', 'NLP', 'DNLP', 'RMINLP', 'QCP', 'RMIQCP'},
'PYOMO': {
'LP',
'MIP',
'RMIP',
'NLP',
'MCP',
'MPEC',
'RMPEC',
'CNS',
'DNLP',
'RMINLP',
'MINLP',
},
'QUADMINOS': {'LP'},
'SBB': {'MINLP', 'MIQCP'},
'SCENSOLVER': {
'LP',
'MIP',
'RMIP',
'NLP',
'MCP',
'CNS',
'DNLP',
'RMINLP',
'MINLP',
'QCP',
'MIQCP',
'RMIQCP',
},
'SCIP': {'MIP', 'NLP', 'CNS', 'DNLP', 'RMINLP', 'MINLP', 'QCP', 'MIQCP', 'RMIQCP'},
'SHOT': {'MINLP', 'MIQCP'},
'SNOPT': {'LP', 'RMIP', 'NLP', 'CNS', 'DNLP', 'RMINLP', 'QCP', 'RMIQCP'},
'SOPLEX': {'LP', 'RMIP'},
'XA': {'LP', 'MIP', 'RMIP'},
'XPRESS': {'LP', 'MIP', 'RMIP', 'QCP', 'MIQCP', 'RMIQCP'},
}