Ejemplo n.º 1
0
 def test_none(self):
     val = None
     try:
         as_numeric(val)
         self.fail("Expected ValueError")
     except:
         pass
Ejemplo n.º 2
0
 def test_none(self):
     val = None
     try:
         as_numeric(val)
         self.fail("Expected ValueError")
     except:
         pass
Ejemplo n.º 3
0
 def test_string(self):
     val = 'foo'
     try:
         as_numeric(val)
         self.fail("Expected ValueError")
     except:
         pass
Ejemplo n.º 4
0
 def test_string(self):
     val = 'foo'
     try:
         as_numeric(val)
         self.fail("Expected ValueError")
     except:
         pass
Ejemplo n.º 5
0
 def set_value(self, cc):
     """
     Add a complementarity condition with a specified index.
     """
     if cc.__class__ is ComplementarityTuple:
         #
         # The ComplementarityTuple has a fixed length, so we initialize
         # the _args component and return
         #
         self._args = (as_numeric(cc.arg0), as_numeric(cc.arg1))
     #
     elif cc.__class__ is tuple:
         if len(cc) != 2:
             raise ValueError(
                 "Invalid tuple for Complementarity %s (expected 2-tuple):"
                 "\n\t%s" % (self.name, cc))
         self._args = tuple(as_numeric(x) for x in cc)
     elif cc is Complementarity.Skip:
         del self.parent_component()[self.index()]
     elif cc.__class__ is list:
         #
         # Call set_value() recursively to apply the error same error
         # checks.
         #
         return self.set_value(tuple(cc))
     else:
         raise ValueError("Unexpected value for Complementarity %s:\n\t%s" %
                          (self.name, cc))
Ejemplo n.º 6
0
 def test_error1(self):
     class A(object): pass
     val = A()
     try:
         as_numeric(val)
         self.fail("Expected TypeError")
     except TypeError:
         pass
Ejemplo n.º 7
0
 def test_error1(self):
     class A(object): pass
     val = A()
     try:
         as_numeric(val)
         self.fail("Expected TypeError")
     except TypeError:
         pass
Ejemplo n.º 8
0
 def test_bool(self):
     val = False
     try:
         as_numeric(val)
         self.fail("Expected ValueError")
     except:
         pass
     val = True
     try:
         as_numeric(val)
         self.fail("Expected ValueError")
     except:
         pass
Ejemplo n.º 9
0
 def test_bool(self):
     val = False
     try:
         as_numeric(val)
         self.fail("Expected ValueError")
     except:
         pass
     val = True
     try:
         as_numeric(val)
         self.fail("Expected ValueError")
     except:
         pass
Ejemplo n.º 10
0
 def test_unknownType(self):
     ref = MyBogusType(42)
     try:
         val = as_numeric(ref)
         self.fail("Expected TypeError")
     except TypeError:
         pass
Ejemplo n.º 11
0
 def test_unknownType(self):
     ref = MyBogusType(42)
     try:
         val = as_numeric(ref)
         self.fail("Expected TypeError")
     except TypeError:
         pass
Ejemplo n.º 12
0
 def test_unknownNumericType(self):
     ref = MyBogusNumericType(42)
     val = as_numeric(ref)
     self.assertEqual(val().val, 42.0)
     #self.assertEqual(val().val, 42)
     from pyomo.core.base.numvalue import native_numeric_types, native_types
     self.assertIn(MyBogusNumericType, native_numeric_types)
     self.assertIn(MyBogusNumericType, native_types)
     native_numeric_types.remove(MyBogusNumericType)
     native_types.remove(MyBogusNumericType)
Ejemplo n.º 13
0
 def test_unknownNumericType(self):
     ref = MyBogusNumericType(42)
     val = as_numeric(ref)
     self.assertEqual(val().val, 42.0)
     #self.assertEqual(val().val, 42)
     from pyomo.core.base.numvalue import native_numeric_types, native_types
     self.assertIn(MyBogusNumericType, native_numeric_types)
     self.assertIn(MyBogusNumericType, native_types)
     native_numeric_types.remove(MyBogusNumericType)
     native_types.remove(MyBogusNumericType)
Ejemplo n.º 14
0
 def body(self):
     """Access the body of a constraint expression."""
     if self._body is not None:
         body = self._body
     else:
         # The incoming RangedInequality had a potentially variable
         # bound.  The "body" is fine, but the bounds may not be
         # (although the responsibility for those checks lies with the
         # lower/upper properties)
         body = self._expr.arg(1)
     return as_numeric(body)
Ejemplo n.º 15
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.is_potentially_variable():
             raise ValueError(
                 "Constraint lower bounds must be "
                 "expressions restricted to data.")
     self._ub = ub
Ejemplo n.º 16
0
 def upper(self):
     """Access the upper bound of a constraint expression."""
     bound = self._ub()
     # Historically, constraint.upper was guaranteed to return a type
     # derived from Pyomo NumericValue (or None).  Replicate that
     # functionality, although clients should in almost all cases
     # move to using ConstraintData.ub instead of accessing
     # lower/body/upper to avoid the unnecessary creation (and
     # inevitable destruction) of the NumericConstant wrappers.
     if bound is None:
         return None
     return as_numeric(bound)
Ejemplo n.º 17
0
    def add(self, index, cc):
        """
        Add a complementarity condition with a specified index.
        """
        if cc.__class__ is ComplementarityTuple:
            #
            # The ComplementarityTuple has a fixed length, so we initialize
            # the _args component and return
            #
            self[index]._args = ( as_numeric(cc.arg0), as_numeric(cc.arg1) )
            return self[index]
        #
        if cc.__class__ is tuple:
            if cc is Complementarity.Skip:
                return
            elif len(cc) != 2:
                raise ValueError(
                    "Invalid tuple for Complementarity %s (expected 2-tuple):"
                    "\n\t%s" % (self.name, cc) )
        elif cc.__class__ is list:
            #
            # Call add() recursively to apply the error same error
            # checks.
            #
            return self.add(index, tuple(cc))
        elif cc is None:
                raise ValueError("""
Invalid complementarity condition.  The complementarity condition
is None instead of a 2-tuple.  Please modify your rule to return
Complementarity.Skip instead of None.

Error thrown for Complementarity "%s"
""" % ( self.name, ) )
        else:
            raise ValueError(
                "Unexpected argument declaring Complementarity %s:\n\t%s"
                % (self.name, cc) )
        #
        self[index]._args = tuple( as_numeric(x) for x in cc )
        return self[index]
Ejemplo n.º 18
0
    def add(self, index, cc):
        """
        Add a complementarity condition with a specified index.
        """
        if cc.__class__ is ComplementarityTuple:
            #
            # The ComplementarityTuple has a fixed length, so we initialize
            # the _args component and return
            #
            self[index]._args = (as_numeric(cc.arg0), as_numeric(cc.arg1))
            return self[index]
        #
        if cc.__class__ is tuple:
            if cc is Complementarity.Skip:
                return
            elif len(cc) != 2:
                raise ValueError(
                    "Invalid tuple for Complementarity %s (expected 2-tuple):"
                    "\n\t%s" % (self.name, cc))
        elif cc.__class__ is list:
            #
            # Call add() recursively to apply the error same error
            # checks.
            #
            return self.add(index, tuple(cc))
        elif cc is None:
            raise ValueError("""
Invalid complementarity condition.  The complementarity condition
is None instead of a 2-tuple.  Please modify your rule to return
Complementarity.Skip instead of None.

Error thrown for Complementarity "%s"
""" % (self.name, ))
        else:
            raise ValueError(
                "Unexpected argument declaring Complementarity %s:\n\t%s" %
                (self.name, cc))
        #
        self[index]._args = tuple(as_numeric(x) for x in cc)
        return self[index]
Ejemplo n.º 19
0
def substitute_template_with_value(expr):
    """A simple substituter to expand expression for current template

    This substituter will replace all _GetItemExpression / IndexTemplate
    nodes with the actual _ComponentData based on the current value of
    the IndexTemplate(s)

    """

    if type(expr) is IndexTemplate:
        return as_numeric(expr())
    else:
        return resolve_template(expr)
Ejemplo n.º 20
0
 def _fix_vars(self, expr, model):
     """ Walk through the S-expression, fixing variables. """
     # TODO - Change this to use a visitor pattern!
     if expr._args is None:
         return expr
     _args = []
     for i in range(len(expr._args)):
         if isinstance(expr._args[i],ExpressionBase):
             _args.append( self._fix_vars(expr._args[i], model) )
         elif (isinstance(expr._args[i],Var) or isinstance(expr._args[i],_VarData)) and expr._args[i].fixed:
             if expr._args[i].value != 0.0:
                 _args.append( as_numeric(expr._args[i].value) )
         else:
             _args.append( expr._args[i] )
     expr._args = _args
     return expr
Ejemplo n.º 21
0
 def _fix_vars(self, expr, model):
     """ Walk through the S-expression, fixing variables. """
     # TODO - Change this to use a visitor pattern!
     if expr._args is None:
         return expr
     _args = []
     for i in range(len(expr._args)):
         if isinstance(expr._args[i],ExpressionBase):
             _args.append( self._fix_vars(expr._args[i], model) )
         elif (isinstance(expr._args[i],Var) or isinstance(expr._args[i],_VarData)) and expr._args[i].fixed:
             if expr._args[i].value != 0.0:
                 _args.append( as_numeric(expr._args[i].value) )
         else:
             _args.append( expr._args[i] )
     expr._args = _args
     return expr
Ejemplo n.º 22
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.is_potentially_variable():
             raise ValueError(
                 "Constraint right-hand side must be "
                 "expressions restricted to data.")
     self._lb = rhs
     self._ub = rhs
     self._equality = True
Ejemplo n.º 23
0
 def test_float(self):
     val = 1.1
     nval = as_numeric(val)
     self.assertEqual(val, nval)
     self.assertEqual(nval / 2, 0.55)
Ejemplo n.º 24
0
 def test_long(self):
     val = long(1e10)
     nval = as_numeric(val)
     self.assertEqual(1.0e10, nval)
     #self.assertEqual(val, as_numeric(val))
     self.assertEqual(nval/2, 5.0e9)
Ejemplo n.º 25
0
 def test_const1(self):
     val = NumericConstant(1.0)
     self.assertEqual(1.0, as_numeric(val))
Ejemplo n.º 26
0
    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:
            #
            # Form equality expression
            #
            if len(expr) == 2:
                arg0 = expr[0]
                arg1 = expr[1]
                # assigning to the rhs property
                # will set the equality flag to True
                if not is_potentially_variable(arg1):
                    self.rhs = arg1
                    self.body = arg0
                elif not is_potentially_variable(arg0):
                    self.rhs = arg0
                    self.body = arg1
                else:
                    self.rhs = ZeroConstant
                    self.body = arg0
                    self.body -= arg1

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

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

                arg2 = expr[2]
                if arg2 is not None:
                    if not is_numeric_data(arg2):
                        raise ValueError(
                            "Constraint '%s' found a 3-tuple (lower,"
                            " expression, upper) but the upper "
                            "value was not numeric data or an "
                            "expression restricted to storage of "
                            "numeric 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   sum_product(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   sum_product(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 logical_expr._using_chained_inequality and \
           (logical_expr._chainedInequality.prev is not None):
            raise TypeError(logical_expr._chainedInequality.error_message())
        #
        # Process relational expressions
        # (i.e. explicit '==', '<', and '<=')
        #
        if relational_expr:
            if _expr_type is logical_expr.EqualityExpression:
                # assigning to the rhs property
                # will set the equality flag to True
                if not is_potentially_variable(expr.arg(1)):
                    self.rhs = expr.arg(1)
                    self.body = expr.arg(0)
                elif not is_potentially_variable(expr.arg(0)):
                    self.rhs = expr.arg(0)
                    self.body = expr.arg(1)
                else:
                    self.rhs = ZeroConstant
                    self.body = expr.arg(0)
                    self.body -= expr.arg(1)

            elif _expr_type is logical_expr.InequalityExpression:
                if expr._strict:
                    raise ValueError(
                        "Constraint '%s' encountered a strict "
                        "inequality expression ('>' or '<'). All"
                        " constraints must be formulated using "
                        "using '<=', '>=', or '=='."
                        % (self.name))
                if not is_potentially_variable(expr.arg(1)):
                    self.lb = None
                    self.body = expr.arg(0)
                    self.ub = expr.arg(1)
                elif not is_potentially_variable(expr.arg(0)):
                    self.lb = expr.arg(0)
                    self.body = expr.arg(1)
                    self.ub = None
                else:
                    self.lb = None
                    self.body  = expr.arg(0)
                    self.body -= expr.arg(1)
                    self.ub = ZeroConstant

            else:   # RangedExpression
                if any(expr._strict):
                    raise ValueError(
                        "Constraint '%s' encountered a strict "
                        "inequality expression ('>' or '<'). All"
                        " constraints must be formulated using "
                        "using '<=', '>=', or '=='."
                        % (self.name))

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

                self.lb = expr.arg(0)
                self.body  = expr.arg(1)
                self.ub = expr.arg(2)

        #
        # 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)
Ejemplo n.º 27
0
    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:
            #
            # Form equality expression
            #
            if len(expr) == 2:
                arg0 = expr[0]
                arg1 = expr[1]
                # assigning to the rhs property
                # will set the equality flag to True
                if not is_potentially_variable(arg1):
                    self.rhs = arg1
                    self.body = arg0
                elif not is_potentially_variable(arg0):
                    self.rhs = arg0
                    self.body = arg1
                else:
                    self.rhs = ZeroConstant
                    self.body = arg0
                    self.body -= arg1

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

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

                arg2 = expr[2]
                if arg2 is not None:
                    if not is_numeric_data(arg2):
                        raise ValueError(
                            "Constraint '%s' found a 3-tuple (lower,"
                            " expression, upper) but the upper "
                            "value was not numeric data or an "
                            "expression restricted to storage of "
                            "numeric data." % (self.name))
                elif arg1 is not None and is_numeric_data(arg1):
                    # Special case (reflect behavior of AML): if the
                    # upper bound is None and the "body" is only data,
                    # then shift the body to the UB and the LB to the
                    # body
                    arg0, arg1, arg2 = arg2, arg0, arg1

                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   sum_product(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   sum_product(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)

        #
        # Process relational expressions
        # (i.e. explicit '==', '<', and '<=')
        #
        if relational_expr:
            if _expr_type is logical_expr.EqualityExpression:
                # assigning to the rhs property
                # will set the equality flag to True
                if not is_potentially_variable(expr.arg(1)):
                    self.rhs = expr.arg(1)
                    self.body = expr.arg(0)
                elif not is_potentially_variable(expr.arg(0)):
                    self.rhs = expr.arg(0)
                    self.body = expr.arg(1)
                else:
                    self.rhs = ZeroConstant
                    self.body = expr.arg(0)
                    self.body -= expr.arg(1)

            elif _expr_type is logical_expr.InequalityExpression:
                if expr._strict:
                    raise ValueError("Constraint '%s' encountered a strict "
                                     "inequality expression ('>' or '<'). All"
                                     " constraints must be formulated using "
                                     "using '<=', '>=', or '=='." %
                                     (self.name))
                if not is_potentially_variable(expr.arg(1)):
                    self.lb = None
                    self.body = expr.arg(0)
                    self.ub = expr.arg(1)
                elif not is_potentially_variable(expr.arg(0)):
                    self.lb = expr.arg(0)
                    self.body = expr.arg(1)
                    self.ub = None
                else:
                    self.lb = None
                    self.body = expr.arg(0)
                    self.body -= expr.arg(1)
                    self.ub = ZeroConstant

            else:  # RangedExpression
                if any(expr._strict):
                    raise ValueError("Constraint '%s' encountered a strict "
                                     "inequality expression ('>' or '<'). All"
                                     " constraints must be formulated using "
                                     "using '<=', '>=', or '=='." %
                                     (self.name))

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

                self.lb = expr.arg(0)
                self.body = expr.arg(1)
                self.ub = expr.arg(2)

        #
        # 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)
Ejemplo n.º 28
0
 def test_float(self):
     val = 1.1
     nval = as_numeric(val)
     self.assertEqual(val, nval)
     self.assertEqual(nval/2, 0.55)
Ejemplo n.º 29
0
 def test_int(self):
     val = 1
     nval = as_numeric(val)
     self.assertEqual(1.0, nval)
     #self.assertEqual(val, nval)
     self.assertEqual(nval/2, 0.5)
Ejemplo n.º 30
0
    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:
            #
            # Form equality expression
            #
            if len(expr) == 2:
                arg0 = expr[0]
                arg1 = expr[1]
                # assigning to the rhs property
                # will set the equality flag to True
                if not is_potentially_variable(arg1):
                    self.rhs = arg1
                    self.body = arg0
                elif not is_potentially_variable(arg0):
                    self.rhs = arg0
                    self.body = arg1
                else:
                    self.rhs = ZeroConstant
                    self.body = arg0
                    self.body -= arg1

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

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

                arg2 = expr[2]
                if arg2 is not None:
                    if not is_numeric_data(arg2):
                        raise ValueError(
                            "Constraint '%s' found a 3-tuple (lower,"
                            " expression, upper) but the upper "
                            "value was not numeric data or an "
                            "expression restricted to storage of "
                            "numeric 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   sum_product(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   sum_product(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 logical_expr._using_chained_inequality and \
           (logical_expr._chainedInequality.prev is not None):
            raise TypeError(logical_expr._chainedInequality.error_message())
        #
        # Process relational expressions
        # (i.e. explicit '==', '<', and '<=')
        #
        if relational_expr:
            if _expr_type is logical_expr.EqualityExpression:
                # assigning to the rhs property
                # will set the equality flag to True
                if not is_potentially_variable(expr.arg(1)):
                    self.rhs = expr.arg(1)
                    self.body = expr.arg(0)
                elif not is_potentially_variable(expr.arg(0)):
                    self.rhs = expr.arg(0)
                    self.body = expr.arg(1)
                else:
                    self.rhs = ZeroConstant
                    self.body = expr.arg(0)
                    self.body -= expr.arg(1)

            elif _expr_type is logical_expr.InequalityExpression:
                if expr._strict:
                    raise ValueError(
                        "Constraint '%s' encountered a strict "
                        "inequality expression ('>' or '<'). All"
                        " constraints must be formulated using "
                        "using '<=', '>=', or '=='."
                        % (self.name))
                if not is_potentially_variable(expr.arg(1)):
                    self.lb = None
                    self.body = expr.arg(0)
                    self.ub = expr.arg(1)
                elif not is_potentially_variable(expr.arg(0)):
                    self.lb = expr.arg(0)
                    self.body = expr.arg(1)
                    self.ub = None
                else:
                    self.lb = None
                    self.body  = expr.arg(0)
                    self.body -= expr.arg(1)
                    self.ub = ZeroConstant

            else:   # RangedExpression
                if any(expr._strict):
                    raise ValueError(
                        "Constraint '%s' encountered a strict "
                        "inequality expression ('>' or '<'). All"
                        " constraints must be formulated using "
                        "using '<=', '>=', or '=='."
                        % (self.name))

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

                self.lb = expr.arg(0)
                self.body  = expr.arg(1)
                self.ub = expr.arg(2)

        #
        # 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)
Ejemplo n.º 31
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])

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

            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

            cName = symbolMap.getSymbol(con, con_labeler)
            if con.equality:
                constraint_names.append('%s' % cName)
                ConstraintIO.write('%s.. %s =e= %s ;\n' % (
                    constraint_names[-1],
                    expression_to_string(con_body, tc, smap=symbolMap),
                    _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),
                        expression_to_string(con_body, tc, smap=symbolMap)
                    ))
                if con.has_ub():
                    constraint_names.append('%s_hi' % cName)
                    ConstraintIO.write('%s.. %s =l= %s ;\n' % (
                        constraint_names[-1],
                        expression_to_string(con_body, tc, smap=symbolMap),
                        _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)
        constraint_names.append(oName)
        ConstraintIO.write('%s.. GAMS_OBJECTIVE =e= %s ;\n' % (
            oName,
            expression_to_string(obj.expr, tc, smap=symbolMap)
        ))

        # 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 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)
            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 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("GAMS writer 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))
Ejemplo n.º 32
0
    def set_value(self, expr):
        """Set the expression on this constraint."""

        _expr_type = expr.__class__
        if hasattr(expr, 'is_relational'):
            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   sum(model.costs) == model.income"
                    "\n   (0, model.price[item], 50)"
                    % (self.name, str(expr)))

        elif _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)

                self._equality = True
                if arg1 is None or (not arg1.is_potentially_variable()):
                    self._lower = self._upper = arg1
                    self._body = arg0
                elif arg0 is None or (not arg0.is_potentially_variable()):
                    self._lower = self._upper = arg0
                    self._body = arg1
                else:
                    self._lower = self._upper = ZeroConstant
                    self._body = arg0 - arg1
            #
            # Form inequality expression
            #
            elif len(expr) == 3:
                arg0 = expr[0]
                if arg0 is not None:
                    arg0 = as_numeric(arg0)
                    if arg0.is_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.is_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._lower = arg0
                self._body  = arg1
                self._upper = arg2
            else:
                raise ValueError(
                    "Constructor rule for constraint '%s' returned "
                    "a tuple of length %d. Expecting a tuple of "
                    "length 2 or 3:\n"
                    "Equality:   (left, right)\n"
                    "Inequality: (lower, expression, upper)"
                    % (self.name, len(expr)))

            relational_expr = False
        #
        # Ignore an 'empty' constraint
        #
        elif _expr_type is type:
            self._body = None
            self._lower = None
            self._upper = None
            self._equality = False

            if expr is Constraint.Skip:
                del self.parent_component()[self.index()]
                return
            elif expr is Constraint.Infeasible:
                del self.parent_component()[self.index()]
                raise ValueError(
                    "Constraint '%s' is always infeasible"
                    % (self.name,) )
            else:
                raise ValueError(
                    "Constraint '%s' does not have a proper "
                    "value. Found '%s'\nExpecting a tuple or "
                    "equation. Examples:"
                    "\n   sum(model.costs) == model.income"
                    "\n   (0, model.price[item], 50)"
                    % (self.name, str(expr)))

        elif expr is None:
            raise ValueError(_rule_returned_none_error % (self.name,))

        elif _expr_type is bool:
            raise ValueError(
                "Invalid constraint expression. The constraint "
                "expression resolved to a trivial Boolean (%s) "
                "instead of a Pyomo object. Please modify your "
                "rule to return Constraint.%s instead of %s."
                "\n\nError thrown for Constraint '%s'"
                % (expr, "Feasible" if expr else "Infeasible",
                   expr, self.name))

        else:
            msg = ("Constraint '%s' does not have a proper "
                   "value. Found '%s'\nExpecting a tuple or "
                   "equation. Examples:"
                   "\n   sum(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)

        #
        # Process relational expressions
        # (i.e. explicit '==', '<', and '<=')
        #
        if relational_expr:
            if _expr_type is logical_expr.EqualityExpression:
                # Equality expression: only 2 arguments!
                self._equality = True

                if expr.arg(1).__class__ in native_numeric_types or not expr.arg(1).is_potentially_variable():
                    self._lower = self._upper = as_numeric(expr.arg(1))
                    self._body = expr.arg(0)
                elif expr.arg(0).__class__ in native_numeric_types or not expr.arg(0).is_potentially_variable():
                    self._lower = self._upper = as_numeric(expr.arg(0))
                    self._body = expr.arg(1)
                else:
                    self._lower = self._upper = ZeroConstant
                    self._body = expr.arg(0) - expr.arg(1)

            elif _expr_type is logical_expr.InequalityExpression:
                if expr._strict:
                    raise ValueError(
                        "Constraint '%s' encountered a strict "
                        "inequality expression ('>' or '<'). All"
                        " constraints must be formulated using "
                        "using '<=', '>=', or '=='."
                        % (self.name))

                arg0 = as_numeric(expr.arg(0))
                arg1 = as_numeric(expr.arg(1))
                if not arg1.is_potentially_variable():
                    self._lower = None
                    self._body  = arg0
                    self._upper = arg1
                elif not arg0.is_potentially_variable():
                    self._lower = arg0
                    self._body  = arg1
                    self._upper = None
                else:
                    self._lower = None
                    self._body = arg0
                    self._body -= arg1
                    self._upper = ZeroConstant


            else:   # RangedExpression
                if any(expr._strict):
                    raise ValueError(
                        "Constraint '%s' encountered a strict "
                        "inequality expression ('>' or '<'). All"
                        " constraints must be formulated using "
                        "using '<=', '>=', or '=='."
                        % (self.name))

                #if expr.arg(0).is_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 expr.arg(2).is_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._lower = as_numeric(expr.arg(0))
                self._body  = expr.arg(1)
                self._upper = as_numeric(expr.arg(2))

        #
        # Reset the values to 'None' if they are 'infinite'
        #
        if (self._lower is not None) and is_constant(self._lower):
            val = self._lower if self._lower.__class__ in native_numeric_types else self._lower()
            if not math.isfinite(val):
                if val > 0:
                    raise ValueError(
                        "Constraint '%s' created with a +Inf lower "
                        "bound." % (self.name))
                self._lower = None
            elif bool(val > 0) == bool(val <= 0):
                raise ValueError(
                    "Constraint '%s' created with a non-numeric "
                    "lower bound." % (self.name))

        if (self._upper is not None) and is_constant(self._upper):
            val = self._upper if self._upper.__class__ in native_numeric_types else self._upper()
            if not math.isfinite(val):
                if val < 0:
                    raise ValueError(
                        "Constraint '%s' created with a -Inf upper "
                        "bound." % (self.name))
                self._upper = None
            elif bool(val > 0) == bool(val <= 0):
                raise ValueError(
                    "Constraint '%s' created with a non-numeric "
                    "upper bound." % (self.name))

        #
        # Error check, to ensure that we don't have a constraint that
        # doesn't depend on any variables / parameters.
        #
        # Error check, to ensure that we don't have an equality
        # constraint with 'infinite' RHS
        #
        if self._equality:
            if self._lower is None:
                raise ValueError(
                    "Equality constraint '%s' defined with "
                    "non-finite term." % (self.name))
            assert self._lower is self._upper
Ejemplo n.º 33
0
    def set_value(self, expr):
        """Set the expression on this constraint."""

        _expr_type = expr.__class__
        if hasattr(expr, 'is_relational'):
            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   sum(model.costs) == model.income"
                                 "\n   (0, model.price[item], 50)" %
                                 (self.name, str(expr)))

        elif _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)

                self._equality = True
                if arg1 is None or (not arg1.is_potentially_variable()):
                    self._lower = self._upper = arg1
                    self._body = arg0
                elif arg0 is None or (not arg0.is_potentially_variable()):
                    self._lower = self._upper = arg0
                    self._body = arg1
                else:
                    self._lower = self._upper = ZeroConstant
                    self._body = arg0 - arg1
            #
            # Form inequality expression
            #
            elif len(expr) == 3:
                arg0 = expr[0]
                if arg0 is not None:
                    arg0 = as_numeric(arg0)
                    if arg0.is_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.is_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._lower = arg0
                self._body = arg1
                self._upper = arg2
            else:
                raise ValueError(
                    "Constructor rule for constraint '%s' returned "
                    "a tuple of length %d. Expecting a tuple of "
                    "length 2 or 3:\n"
                    "Equality:   (left, right)\n"
                    "Inequality: (lower, expression, upper)" %
                    (self.name, len(expr)))

            relational_expr = False
        #
        # Ignore an 'empty' constraint
        #
        elif _expr_type is type:
            self._body = None
            self._lower = None
            self._upper = None
            self._equality = False

            if expr is Constraint.Skip:
                del self.parent_component()[self.index()]
                return
            elif expr is Constraint.Infeasible:
                del self.parent_component()[self.index()]
                raise ValueError("Constraint '%s' is always infeasible" %
                                 (self.name, ))
            else:
                raise ValueError("Constraint '%s' does not have a proper "
                                 "value. Found '%s'\nExpecting a tuple or "
                                 "equation. Examples:"
                                 "\n   sum(model.costs) == model.income"
                                 "\n   (0, model.price[item], 50)" %
                                 (self.name, str(expr)))

        elif expr is None:
            raise ValueError(_rule_returned_none_error % (self.name, ))

        elif _expr_type is bool:
            #
            # There are cases where a user thinks they are generating
            # a valid 2-sided inequality, but Python's internal
            # systems for handling chained inequalities is doing
            # something very different and resolving it to True /
            # False.  In this case, chainedInequality will be
            # non-None, but the expression will be a bool.  For
            # example, model.a < 1 > 0.
            #
            if logical_expr._using_chained_inequality \
               and logical_expr._chainedInequality.prev is not None:

                buf = StringIO()
                logical_expr._chainedInequality.prev.pprint(buf)
                #
                # We are about to raise an exception, so it's OK to
                # reset chainedInequality
                #
                logical_expr._chainedInequality.prev = None
                raise ValueError(
                    "Invalid chained (2-sided) inequality detected. "
                    "The expression is resolving to %s instead of a "
                    "Pyomo Expression object. This can occur when "
                    "the middle term of a chained inequality is a "
                    "constant or immutable parameter, for example, "
                    "'model.a <= 1 >= 0'.  The proper form for "
                    "2-sided inequalities is '0 <= model.a <= 1'."
                    "\n\nError thrown for Constraint '%s'"
                    "\n\nUnresolved (dangling) inequality "
                    "expression: %s" % (expr, self.name, buf))
            else:
                raise ValueError(
                    "Invalid constraint expression. The constraint "
                    "expression resolved to a trivial Boolean (%s) "
                    "instead of a Pyomo object. Please modify your "
                    "rule to return Constraint.%s instead of %s."
                    "\n\nError thrown for Constraint '%s'" %
                    (expr, "Feasible" if expr else "Infeasible", expr,
                     self.name))

        else:
            msg = ("Constraint '%s' does not have a proper "
                   "value. Found '%s'\nExpecting a tuple or "
                   "equation. Examples:"
                   "\n   sum(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 logical_expr._using_chained_inequality \
           and logical_expr._chainedInequality.prev is not None:
            raise TypeError(logical_expr._chainedInequality.error_message())
        #
        # Process relational expressions
        # (i.e. explicit '==', '<', and '<=')
        #
        if relational_expr:
            if _expr_type is logical_expr.EqualityExpression:
                # Equality expression: only 2 arguments!
                self._equality = True

                if expr.arg(
                        1).__class__ in native_numeric_types or not expr.arg(
                            1).is_potentially_variable():
                    self._lower = self._upper = as_numeric(expr.arg(1))
                    self._body = expr.arg(0)
                elif expr.arg(
                        0).__class__ in native_numeric_types or not expr.arg(
                            0).is_potentially_variable():
                    self._lower = self._upper = as_numeric(expr.arg(0))
                    self._body = expr.arg(1)
                else:
                    self._lower = self._upper = ZeroConstant
                    self._body = expr.arg(0) - expr.arg(1)

            elif _expr_type is logical_expr.InequalityExpression:
                if expr._strict:
                    raise ValueError("Constraint '%s' encountered a strict "
                                     "inequality expression ('>' or '<'). All"
                                     " constraints must be formulated using "
                                     "using '<=', '>=', or '=='." %
                                     (self.name))

                if not expr.arg(1).is_potentially_variable():
                    self._lower = None
                    self._body = expr.arg(0)
                    self._upper = as_numeric(expr.arg(1))
                elif not expr.arg(0).is_potentially_variable():
                    self._lower = as_numeric(expr.arg(0))
                    self._body = expr.arg(1)
                    self._upper = None
                else:
                    self._lower = None
                    self._body = expr.arg(0)
                    self._body -= expr.arg(1)
                    self._upper = ZeroConstant

            else:  # RangedExpression
                if any(expr._strict):
                    raise ValueError("Constraint '%s' encountered a strict "
                                     "inequality expression ('>' or '<'). All"
                                     " constraints must be formulated using "
                                     "using '<=', '>=', or '=='." %
                                     (self.name))

                #if expr.arg(0).is_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 expr.arg(2).is_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._lower = as_numeric(expr.arg(0))
                self._body = expr.arg(1)
                self._upper = as_numeric(expr.arg(2))

        #
        # Reset the values to 'None' if they are 'infinite'
        #
        if (self._lower is not None) and is_constant(self._lower):
            val = self._lower if self._lower.__class__ in native_numeric_types else self._lower(
            )
            if not pyutilib.math.is_finite(val):
                if val > 0:
                    raise ValueError(
                        "Constraint '%s' created with a +Inf lower "
                        "bound." % (self.name))
                self._lower = None
            elif bool(val > 0) == bool(val <= 0):
                raise ValueError("Constraint '%s' created with a non-numeric "
                                 "lower bound." % (self.name))

        if (self._upper is not None) and is_constant(self._upper):
            val = self._upper if self._upper.__class__ in native_numeric_types else self._upper(
            )
            if not pyutilib.math.is_finite(val):
                if val < 0:
                    raise ValueError(
                        "Constraint '%s' created with a -Inf upper "
                        "bound." % (self.name))
                self._upper = None
            elif bool(val > 0) == bool(val <= 0):
                raise ValueError("Constraint '%s' created with a non-numeric "
                                 "upper bound." % (self.name))

        #
        # Error check, to ensure that we don't have a constraint that
        # doesn't depend on any variables / parameters.
        #
        # Error check, to ensure that we don't have an equality
        # constraint with 'infinite' RHS
        #
        if self._equality:
            if self._lower is None:
                raise ValueError("Equality constraint '%s' defined with "
                                 "non-finite term." % (self.name))
            assert self._lower is self._upper
Ejemplo n.º 34
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])

        model_ctypes = model.collect_ctypes(active=True)
        if False:
            #
            # WEH - Disabling this check.  For now, we're allowing
            # variables defined on non-block objects.
            #
            # 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, Connector
            ])
            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))

        # HACK: Temporary check for Connectors in active constriants.
        # This should be removed after the writer is moved to an
        # explicit GAMS-specific expression walker for generating the
        # constraint strings.
        has_Connectors = Connector in model_ctypes

        # 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

            # HACK: Temporary check for Connectors in active constriants.
            if has_Connectors:
                raise RuntimeError("Cannot handle connectors right now.")
                #_check_for_connectors(con)

            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

            cName = symbolMap.getSymbol(con, con_labeler)
            if con.equality:
                constraint_names.append('%s' % cName)
                ConstraintIO.write(
                    '%s.. %s =e= %s ;\n' %
                    (constraint_names[-1],
                     expression_to_string(
                         con_body, smap=symbolMap), _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),
                         expression_to_string(con_body, smap=symbolMap)))
                if con.has_ub():
                    constraint_names.append('%s_hi' % cName)
                    ConstraintIO.write(
                        '%s.. %s =l= %s ;\n' %
                        (constraint_names[-1],
                         expression_to_string(
                             con_body, smap=symbolMap), _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)
        constraint_names.append(oName)
        ConstraintIO.write(
            '%s.. GAMS_OBJECTIVE =e= %s ;\n' %
            (oName, expression_to_string(obj.expr, smap=symbolMap)))

        # 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))
Ejemplo n.º 35
0
    def set_value(self, expr):
        """Set the expression on this constraint."""

        if expr is None:
            self._body = None
            self._lower = None
            self._upper = None
            self._equality = False
            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)

                self._equality = True
                if arg1 is None or (not arg1.is_potentially_variable()):
                    self._lower = self._upper = arg1
                    self._body = arg0
                elif arg0 is None or (not arg0.is_potentially_variable()):
                    self._lower = self._upper = arg0
                    self._body = arg1
                else:
                    self._lower = self._upper = ZeroConstant
                    self._body = arg0 - arg1
            #
            # Form inequality expression
            #
            elif len(expr) == 3:
                arg0 = expr[0]
                if arg0 is not None:
                    arg0 = as_numeric(arg0)
                    if arg0.is_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.is_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._lower = arg0
                self._body = arg1
                self._upper = arg2
            else:
                raise ValueError(
                    "Constructor rule for constraint '%s' returned "
                    "a tuple of length %d. Expecting a tuple of "
                    "length 2 or 3:\n"
                    "Equality:   (left, right)\n"
                    "Inequality: (lower, expression, upper)" %
                    (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   sum(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   sum(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._using_chained_inequality and EXPR._chainedInequality.prev is not None:
            raise TypeError(EXPR._chainedInequality.error_message())
        #
        # Process relational expressions
        # (i.e. explicit '==', '<', and '<=')
        #
        if relational_expr:
            if _expr_type is EXPR.EqualityExpression:
                # Equality expression: only 2 arguments!
                self._equality = True

                if expr.arg(
                        1).__class__ in native_numeric_types or not expr.arg(
                            1).is_potentially_variable():
                    self._lower = self._upper = expr.arg(1)
                    self._body = expr.arg(0)
                elif expr.arg(
                        0).__class__ in native_numeric_types or not expr.arg(
                            0).is_potentially_variable():
                    self._lower = self._upper = expr.arg(0)
                    self._body = expr.arg(1)
                else:
                    self._lower = self._upper = ZeroConstant
                    self._body = expr.arg(0) - expr.arg(1)

            elif _expr_type is EXPR.InequalityExpression:
                if expr._strict:
                    raise ValueError("Constraint '%s' encountered a strict "
                                     "inequality expression ('>' or '<'). All"
                                     " constraints must be formulated using "
                                     "using '<=', '>=', or '=='." %
                                     (self.name))

                if not expr.arg(1).is_potentially_variable():
                    self._lower = None
                    self._body = expr.arg(0)
                    self._upper = expr.arg(1)
                elif not expr.arg(0).is_potentially_variable():
                    self._lower = expr.arg(0)
                    self._body = expr.arg(1)
                    self._upper = None
                else:
                    self._lower = None
                    self._body = expr.arg(0)
                    self._body -= expr.arg(1)
                    self._upper = ZeroConstant

            else:  # RangedExpression
                if any(expr._strict):
                    raise ValueError("Constraint '%s' encountered a strict "
                                     "inequality expression ('>' or '<'). All"
                                     " constraints must be formulated using "
                                     "using '<=', '>=', or '=='." %
                                     (self.name))

                #if expr.arg(0).is_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 expr.arg(2).is_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._lower = expr.arg(0)
                self._body = expr.arg(1)
                self._upper = expr.arg(2)

        #
        # Reset the values to 'None' if they are 'infinite'
        #
        if (self._lower is not None) and is_constant(self._lower):
            val = self._lower if self._lower.__class__ in native_numeric_types else self._lower(
            )
            if not pyutilib.math.is_finite(val):
                if val > 0:
                    raise ValueError(
                        "Constraint '%s' created with a +Inf lower "
                        "bound." % (self.name))
                self._lower = None
            elif bool(val > 0) == bool(val <= 0):
                raise ValueError("Constraint '%s' created with a non-numeric "
                                 "lower bound." % (self.name))

        if (self._upper is not None) and is_constant(self._upper):
            val = self._upper if self._upper.__class__ in native_numeric_types else self._upper(
            )
            if not pyutilib.math.is_finite(val):
                if val < 0:
                    raise ValueError(
                        "Constraint '%s' created with a -Inf upper "
                        "bound." % (self.name))
                self._upper = None
            elif bool(val > 0) == bool(val <= 0):
                raise ValueError("Constraint '%s' created with a non-numeric "
                                 "upper bound." % (self.name))

        #
        # Error check, to ensure that we don't have a constraint that
        # doesn't depend on any variables / parameters.
        #
        # Error check, to ensure that we don't have an equality
        # constraint with 'infinite' RHS
        #
        if self._equality:
            if self._lower is None:
                raise ValueError("Equality constraint '%s' defined with "
                                 "non-finite term." % (self.name))
            assert self._lower is self._upper
Ejemplo n.º 36
0
 def test_int(self):
     val = 1
     nval = as_numeric(val)
     self.assertEqual(1.0, nval)
     #self.assertEqual(val, nval)
     self.assertEqual(nval / 2, 0.5)
Ejemplo n.º 37
0
 def body(self, body):
     if body is not None:
         body = as_numeric(body)
     self._body = body
Ejemplo n.º 38
0
 def test_long(self):
     val = long(1e10)
     nval = as_numeric(val)
     self.assertEqual(1.0e10, nval)
     #self.assertEqual(val, as_numeric(val))
     self.assertEqual(nval / 2, 5.0e9)
Ejemplo n.º 39
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])

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

            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

            cName = symbolMap.getSymbol(con, con_labeler)
            if con.equality:
                constraint_names.append('%s' % cName)
                ConstraintIO.write(
                    '%s.. %s =e= %s ;\n' %
                    (constraint_names[-1],
                     expression_to_string(
                         con_body, tc, smap=symbolMap), _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),
                         expression_to_string(con_body, tc, smap=symbolMap)))
                if con.has_ub():
                    constraint_names.append('%s_hi' % cName)
                    ConstraintIO.write(
                        '%s.. %s =l= %s ;\n' %
                        (constraint_names[-1],
                         expression_to_string(con_body, tc, smap=symbolMap),
                         _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)
        constraint_names.append(oName)
        ConstraintIO.write(
            '%s.. GAMS_OBJECTIVE =e= %s ;\n' %
            (oName, expression_to_string(obj.expr, tc, smap=symbolMap)))

        # 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 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)
            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 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("GAMS writer 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))
Ejemplo n.º 40
0
 def test_const1(self):
     val = NumericConstant(1.0)
     self.assertEqual(1.0, as_numeric(val))
Ejemplo n.º 41
0
 def body(self, body):
     if body is not None:
         body = as_numeric(body)
     self._body = body
Ejemplo n.º 42
0
    def set_value(self, expr):
        """Set the expression on this constraint."""

        if expr is None:
            self._body = None
            self._lower = None
            self._upper = None
            self._equality = False
            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)

                self._equality = True
                if arg1 is None or (not arg1.is_potentially_variable()):
                    self._lower = self._upper = arg1
                    self._body = arg0
                elif arg0 is None or (not arg0.is_potentially_variable()):
                    self._lower = self._upper = arg0
                    self._body = arg1
                else:
                    self._lower = self._upper = ZeroConstant
                    self._body = arg0 - arg1
            #
            # Form inequality expression
            #
            elif len(expr) == 3:
                arg0 = expr[0]
                if arg0 is not None:
                    arg0 = as_numeric(arg0)
                    if arg0.is_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.is_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._lower = arg0
                self._body  = arg1
                self._upper = arg2
            else:
                raise ValueError(
                    "Constructor rule for constraint '%s' returned "
                    "a tuple of length %d. Expecting a tuple of "
                    "length 2 or 3:\n"
                    "Equality:   (left, right)\n"
                    "Inequality: (lower, expression, upper)"
                    % (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   sum(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   sum(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 logical_expr._using_chained_inequality and logical_expr._chainedInequality.prev is not None:
            raise TypeError(logical_expr._chainedInequality.error_message())
        #
        # Process relational expressions
        # (i.e. explicit '==', '<', and '<=')
        #
        if relational_expr:
            if _expr_type is logical_expr.EqualityExpression:
                # Equality expression: only 2 arguments!
                self._equality = True

                if expr.arg(1).__class__ in native_numeric_types or not expr.arg(1).is_potentially_variable():
                    self._lower = self._upper = as_numeric(expr.arg(1))
                    self._body = expr.arg(0)
                elif expr.arg(0).__class__ in native_numeric_types or not expr.arg(0).is_potentially_variable():
                    self._lower = self._upper = as_numeric(expr.arg(0))
                    self._body = expr.arg(1)
                else:
                    self._lower = self._upper = ZeroConstant
                    self._body = expr.arg(0) - expr.arg(1)

            elif _expr_type is logical_expr.InequalityExpression:
                if expr._strict:
                    raise ValueError(
                        "Constraint '%s' encountered a strict "
                        "inequality expression ('>' or '<'). All"
                        " constraints must be formulated using "
                        "using '<=', '>=', or '=='."
                        % (self.name))

                if not expr.arg(1).is_potentially_variable():
                    self._lower = None
                    self._body  = expr.arg(0)
                    self._upper = as_numeric(expr.arg(1))
                elif not expr.arg(0).is_potentially_variable():
                    self._lower = as_numeric(expr.arg(0))
                    self._body  = expr.arg(1)
                    self._upper = None
                else:
                    self._lower = None
                    self._body = expr.arg(0)
                    self._body -= expr.arg(1)
                    self._upper = ZeroConstant

            
            else:   # RangedExpression
                if any(expr._strict):
                    raise ValueError(
                        "Constraint '%s' encountered a strict "
                        "inequality expression ('>' or '<'). All"
                        " constraints must be formulated using "
                        "using '<=', '>=', or '=='."
                        % (self.name))

                #if expr.arg(0).is_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 expr.arg(2).is_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._lower = as_numeric(expr.arg(0))
                self._body  = expr.arg(1)
                self._upper = as_numeric(expr.arg(2))

        #
        # Reset the values to 'None' if they are 'infinite'
        #
        if (self._lower is not None) and is_constant(self._lower):
            val = self._lower if self._lower.__class__ in native_numeric_types else self._lower()
            if not pyutilib.math.is_finite(val):
                if val > 0:
                    raise ValueError(
                        "Constraint '%s' created with a +Inf lower "
                        "bound." % (self.name))
                self._lower = None
            elif bool(val > 0) == bool(val <= 0):
                raise ValueError(
                    "Constraint '%s' created with a non-numeric "
                    "lower bound." % (self.name))

        if (self._upper is not None) and is_constant(self._upper):
            val = self._upper if self._upper.__class__ in native_numeric_types else self._upper()
            if not pyutilib.math.is_finite(val):
                if val < 0:
                    raise ValueError(
                        "Constraint '%s' created with a -Inf upper "
                        "bound." % (self.name))
                self._upper = None
            elif bool(val > 0) == bool(val <= 0):
                raise ValueError(
                    "Constraint '%s' created with a non-numeric "
                    "upper bound." % (self.name))

        #
        # Error check, to ensure that we don't have a constraint that
        # doesn't depend on any variables / parameters.
        #
        # Error check, to ensure that we don't have an equality
        # constraint with 'infinite' RHS
        #
        if self._equality:
            if self._lower is None:
                raise ValueError(
                    "Equality constraint '%s' defined with "
                    "non-finite term." % (self.name))
            assert self._lower is self._upper
Ejemplo n.º 43
0
 def expr(self, expr):
     self._expr = as_numeric(expr) if (expr is not None) else None