Example #1
0
 def to_string(self,
               ostream=None,
               verbose=None,
               precedence=0,
               labeler=None):
     """Convert this expression into a string."""
     if ostream is None:
         ostream = sys.stdout
     _verbose = pyomo.core.kernel.expr_common.TO_STRING_VERBOSE if \
         verbose is None else verbose
     if _verbose:
         ostream.write(self._to_string_label())
         ostream.write("{")
     if self._expr is None:
         ostream.write("Undefined")
     elif isinstance(self._expr, NumericValue):
         self._expr.to_string(ostream=ostream,
                              verbose=verbose,
                              precedence=precedence,
                              labeler=labeler)
     else:
         as_numeric(self._expr).to_string(ostream=ostream,
                                          verbose=verbose,
                                          precedence=precedence,
                                          labeler=labeler)
     if _verbose:
         ostream.write("}")
Example #2
0
 def ub(self, ub):
     if self.equality:
         raise ValueError("The ub property can not be set "
                          "when the equality property is True.")
     if ub is not None:
         tmp = as_numeric(ub)
         if tmp._potentially_variable():
             raise ValueError("Constraint lower bounds must be "
                              "expressions restricted to data.")
     self._ub = ub
Example #3
0
 def rhs(self, rhs):
     if rhs is None:
         # None has a different meaning depending on the
         # context (lb or ub), so there is no way to
         # interpret this
         raise ValueError("Constraint right-hand side can not "
                          "be assigned a value of None.")
     else:
         tmp = as_numeric(rhs)
         if tmp._potentially_variable():
             raise ValueError("Constraint right-hand side must be "
                              "expressions restricted to data.")
     self._lb = rhs
     self._ub = rhs
     self._equality = True
    def expr(self, expr):

        self._equality = False
        if expr is None:
            self.body = None
            self.lb = None
            self.ub = None
            return

        _expr_type = expr.__class__
        if _expr_type is tuple: # or expr_type is list:
            #
            # Form equality expression
            #
            if len(expr) == 2:
                arg0 = expr[0]
                if arg0 is not None:
                    arg0 = as_numeric(arg0)
                arg1 = expr[1]
                if arg1 is not None:
                    arg1 = as_numeric(arg1)

                # assigning to the rhs property
                # will set the equality flag to True
                if arg1 is None or (not arg1._potentially_variable()):
                    self.rhs = arg1
                    self.body = arg0
                elif arg0 is None or (not arg0._potentially_variable()):
                    self.rhs = arg0
                    self.body = arg1
                else:
                    with EXPR.bypass_clone_check():
                        self.rhs = ZeroConstant
                        self.body = arg0
                        self.body -= arg1

            #
            # Form inequality expression
            #
            elif len(expr) == 3:
                arg0 = expr[0]
                if arg0 is not None:
                    arg0 = as_numeric(arg0)
                    if arg0._potentially_variable():
                        raise ValueError(
                            "Constraint '%s' found a 3-tuple (lower,"
                            " expression, upper) but the lower "
                            "value was not data or an expression "
                            "restricted to storage of data."
                            % (self.name))

                arg1 = expr[1]
                if arg1 is not None:
                    arg1 = as_numeric(arg1)

                arg2 = expr[2]
                if arg2 is not None:
                    arg2 = as_numeric(arg2)
                    if arg2._potentially_variable():
                        raise ValueError(
                            "Constraint '%s' found a 3-tuple (lower,"
                            " expression, upper) but the upper "
                            "value was not data or an expression "
                            "restricted to storage of data."
                            % (self.name))

                self.lb = arg0
                self.body  = arg1
                self.ub = arg2
            else:
                raise ValueError(
                    "Constraint '%s' assigned a tuple "
                    "of length %d. Expecting a tuple of "
                    "length 2 or 3:\n"
                    "Equality:   (body, rhs)\n"
                    "Inequality: (lb, body, ub)"
                    % (self.name, len(expr)))

            relational_expr = False
        else:
            try:
                relational_expr = expr.is_relational()
                if not relational_expr:
                    raise ValueError(
                        "Constraint '%s' does not have a proper "
                        "value. Found '%s'\nExpecting a tuple or "
                        "equation. Examples:"
                        "\n   summation(model.costs) == model.income"
                        "\n   (0, model.price[item], 50)"
                        % (self.name, str(expr)))
            except AttributeError:
                msg = ("Constraint '%s' does not have a proper "
                       "value. Found '%s'\nExpecting a tuple or "
                       "equation. Examples:"
                       "\n   summation(model.costs) == model.income"
                       "\n   (0, model.price[item], 50)"
                       % (self.name, str(expr)))
                if type(expr) is bool:
                    msg += ("\nNote: constant Boolean expressions "
                            "are not valid constraint expressions. "
                            "Some apparently non-constant compound "
                            "inequalities (e.g. 'expr >= 0 <= 1') "
                            "can return boolean values; the proper "
                            "form for compound inequalities is "
                            "always 'lb <= expr <= ub'.")
                raise ValueError(msg)
        #
        # Special check for chainedInequality errors like "if var <
        # 1:" within rules.  Catching them here allows us to provide
        # the user with better (and more immediate) debugging
        # information.  We don't want to check earlier because we
        # want to provide a specific debugging message if the
        # construction rule returned True/False; for example, if the
        # user did ( var < 1 > 0 ) (which also results in a non-None
        # chainedInequality value)
        #
        if EXPR.generate_relational_expression.chainedInequality is not None:
            raise TypeError(EXPR.chainedInequalityErrorMessage())
        #
        # Process relational expressions
        # (i.e. explicit '==', '<', and '<=')
        #
        if relational_expr:
            if _expr_type is EXPR._EqualityExpression:
                # Equality expression: only 2 arguments!
                _args = expr._args
                # Explicitly dereference the original arglist (otherwise
                # this runs afoul of the getrefcount logic)
                expr._args = []
                # assigning to the rhs property
                # will set the equality flag to True
                if not _args[1]._potentially_variable():
                    self.rhs = _args[1]
                    self.body = _args[0]
                elif not _args[0]._potentially_variable():
                    self.rhs = _args[0]
                    self.body = _args[1]
                else:
                    with EXPR.bypass_clone_check():
                        self.rhs = ZeroConstant
                        self.body = _args[0]
                        self.body -= _args[1]
            else:
                # Inequality expression: 2 or 3 arguments
                if expr._strict:
                    try:
                        _strict = any(expr._strict)
                    except:
                        _strict = True
                    if _strict:
                        #
                        # We can relax this when:
                        #   (a) we have a need for this
                        #   (b) we have problem writer that
                        #       explicitly handles this
                        #   (c) we make sure that all problem writers
                        #       that don't handle this make it known
                        #       to the user through an error or
                        #       warning
                        #
                        raise ValueError(
                            "Constraint '%s' encountered a strict "
                            "inequality expression ('>' or '<'). All"
                            " constraints must be formulated using "
                            "using '<=', '>=', or '=='."
                            % (self.name))

                _args = expr._args
                # Explicitly dereference the original arglist (otherwise
                # this runs afoul of the getrefcount logic)
                expr._args = []
                if len(_args) == 3:

                    if _args[0]._potentially_variable():
                        raise ValueError(
                            "Constraint '%s' found a double-sided "
                            "inequality expression (lower <= "
                            "expression <= upper) but the lower "
                            "bound was not data or an expression "
                            "restricted to storage of data."
                            % (self.name))
                    if _args[2]._potentially_variable():
                        raise ValueError(
                            "Constraint '%s' found a double-sided "\
                            "inequality expression (lower <= "
                            "expression <= upper) but the upper "
                            "bound was not data or an expression "
                            "restricted to storage of data."
                            % (self.name))

                    self.lb = _args[0]
                    self.body  = _args[1]
                    self.ub = _args[2]

                else:

                    if not _args[1]._potentially_variable():
                        self.lb = None
                        self.body  = _args[0]
                        self.ub = _args[1]
                    elif not _args[0]._potentially_variable():
                        self.lb = _args[0]
                        self.body  = _args[1]
                        self.ub = None
                    else:
                        with EXPR.bypass_clone_check():
                            self.lb = None
                            self.body  = _args[0]
                            self.body -= _args[1]
                            self.ub = ZeroConstant

        #
        # Replace numeric bound values with a NumericConstant object,
        # and reset the values to 'None' if they are 'infinite'
        #
        if (self.lb is not None) and is_constant(self.lb):
            val = self.lb()
            if not pyutilib.math.is_finite(val):
                if val > 0:
                    raise ValueError(
                        "Constraint '%s' created with a +Inf lower "
                        "bound." % (self.name))
                self.lb = None

        if (self.ub is not None) and is_constant(self.ub):
            val = self.ub()
            if not pyutilib.math.is_finite(val):
                if val < 0:
                    raise ValueError(
                        "Constraint '%s' created with a -Inf upper "
                        "bound." % (self.name))
                self.ub = None

        #
        # Error check, to ensure that we don't have an equality
        # constraint with 'infinite' RHS
        #
        assert not (self.equality and (self.lb is None))
        assert (not self.equality) or (self.lb is self.ub)
 def body(self, body):
     if body is not None:
         body = as_numeric(body)
     self._body = body
Example #6
0
    def _write_model(self,
                     model,
                     output_file,
                     solver_capability,
                     var_list,
                     var_label,
                     symbolMap,
                     con_labeler,
                     sort,
                     skip_trivial_constraints,
                     warmstart,
                     solver,
                     mtype,
                     add_options,
                     put_results):
        constraint_names = []
        ConstraintIO = StringIO()
        linear = True
        linear_degree = set([0,1])

        # Sanity check: all active components better be things we know
        # how to deal with, plus Suffix if solving
        valid_ctypes = set([
            Block, Constraint, Expression, Objective, Param,
            Set, RangeSet, Var, Suffix ])
        model_ctypes = model.collect_ctypes(active=True)
        if not model_ctypes.issubset(valid_ctypes):
            invalids = [t.__name__ for t in (model_ctypes - valid_ctypes)]
            raise RuntimeError(
                "Unallowable component(s) %s.\nThe GAMS writer cannot "
                "export models with this component type" %
                ", ".join(invalids))

        # 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):

            if (not con.has_lb()) and \
               (not con.has_ub()):
                assert not con.equality
                continue # non-binding, so skip

            con_body = as_numeric(con.body)
            if skip_trivial_constraints and con_body.is_fixed():
                continue
            if linear:
                if con_body.polynomial_degree() not in linear_degree:
                    linear = False

            body = StringIO()
            con_body.to_string(body, labeler=var_label)
            cName = symbolMap.getSymbol(con, con_labeler)
            if con.equality:
                constraint_names.append('%s' % cName)
                ConstraintIO.write('%s.. %s =e= %s ;\n' % (
                    constraint_names[-1],
                    body.getvalue(),
                    _get_bound(con.upper)
                ))
            else:
                if con.has_lb():
                    constraint_names.append('%s_lo' % cName)
                    ConstraintIO.write('%s.. %s =l= %s ;\n' % (
                        constraint_names[-1],
                        _get_bound(con.lower),
                        body.getvalue()
                    ))
                if con.has_ub():
                    constraint_names.append('%s_hi' % cName)
                    ConstraintIO.write('%s.. %s =l= %s ;\n' % (
                        constraint_names[-1],
                        body.getvalue(),
                        _get_bound(con.upper)
                    ))

        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.expr.polynomial_degree() not in linear_degree:
                linear = False
        oName = symbolMap.getSymbol(obj, con_labeler)
        body = StringIO()
        obj.expr.to_string(body, labeler=var_label)
        constraint_names.append(oName)
        ConstraintIO.write('%s.. GAMS_OBJECTIVE =e= %s ;\n' % (
            oName,
            body.getvalue()
        ))

        # Categorize the variables that we found
        categorized_vars = Categorizer(var_list, symbolMap)

        # Write the GAMS model
        # $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))
        output_file.write(";\n\n")

        for line in ConstraintIO.getvalue().splitlines():
            if '**' in line:
                # Investigate power functions for an integer exponent, in which
                # case replace with power(x, int) function to improve domain
                # issues. Skip first term since it's always "con_name.."
                line = replace_power(line) + ';'
            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)
            if category == 'positive':
                if var.has_ub():
                    output_file.write("%s.up = %s;\n" %
                                      (var_name, _get_bound(var.ub)))
            elif category == 'ints':
                if not var.has_lb():
                    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 value(var.lb) != 0:
                    output_file.write("%s.lo = %s;\n" %
                                      (var_name, _get_bound(var.lb)))
                if not var.has_ub():
                    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, _get_bound(var.ub)))
            elif category == 'binary':
                if var.has_lb() and value(var.lb) != 0:
                    output_file.write("%s.lo = %s;\n" %
                                      (var_name, _get_bound(var.lb)))
                if var.has_ub() and value(var.ub) != 1:
                    output_file.write("%s.up = %s;\n" %
                                      (var_name, _get_bound(var.ub)))
            elif category == 'reals':
                if var.has_lb():
                    output_file.write("%s.lo = %s;\n" %
                                      (var_name, _get_bound(var.lb)))
                if var.has_ub():
                    output_file.write("%s.up = %s;\n" %
                                      (var_name, _get_bound(var.ub)))
            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, var.value))
            if var.is_fixed():
                # This probably doesn't run, since all fixed vars are by default
                # replaced with their value and not assigned a symbol.
                # But leave this here in case we change handling of fixed vars
                assert var.value is not None, "Cannot fix variable at None"
                output_file.write("%s.fx = %s;\n" % (var_name, var.value))

        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 solver is not None:
            if mtype.upper() not in valid_solvers[solver.upper()]:
                raise ValueError("ProblemWriter_gams passed solver (%s) "
                                 "unsuitable for model type (%s)"
                                 % (solver, mtype))
            output_file.write("option %s=%s;\n" % (mtype, solver))

        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:
            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))
Example #7
0
 def expr(self, expr):
     self._expr = as_numeric(expr) if (expr is not None) else None