Пример #1
0
class ISOS(ICategorizedObject):
    """
    The interface for Special Ordered Sets.
    """
    __slots__ = ()

    #
    # Implementations can choose to define these
    # properties as using __slots__, __dict__, or
    # by overriding the @property method
    #

    variables = _abstract_readwrite_property(doc="The sos variables")
    weights = _abstract_readwrite_property(doc="The sos variables")
    level = _abstract_readwrite_property(doc="The sos level (e.g., 1,2,...)")

    #
    # Interface
    #

    def items(self):
        """Iterator over the sos variables and weights as tuples"""
        return zip(self.variables, self.weights)

    def __contains__(self, v):
        """Check if the sos contains the variable v"""
        for x in self.variables:
            if id(x) == id(v):
                return True

    def __len__(self):
        """The number of members in the set"""
        return len(self.variables)
Пример #2
0
class IExpression(ICategorizedObject, IIdentityExpression):
    """
    The interface for mutable expressions.
    """
    __slots__ = ()

    #
    # Implementations can choose to define these
    # properties as using __slots__, __dict__, or
    # by overriding the @property method
    #

    expr = _abstract_readwrite_property(doc="The stored expression")

    #
    # Override some of the NumericValue methods implemented
    # by the base class
    #

    def is_constant(self):
        """A boolean indicating whether this expression is constant."""
        return False

    def is_potentially_variable(self):
        """A boolean indicating whether this expression can
        reference variables."""
        return True

    def clone(self):
        """Return a clone of this expression (no-op)."""
        return self
Пример #3
0
class IParameter(ICategorizedObject, NumericValue):
    """
    The interface for mutable parameters.
    """
    __slots__ = ()

    #
    # Implementations can choose to define these
    # properties as using __slots__, __dict__, or
    # by overriding the @property method
    #

    value = _abstract_readwrite_property(doc="The value of the parameter")

    #
    # Implement the NumericValue abstract methods
    #

    def __call__(self, exception=True):
        """Compute the value of this parameter."""
        return self.value

    def is_constant(self):
        """A boolean indicating that this parameter is constant."""
        return False

    def is_parameter_type(self):
        """A boolean indicating that this is a parameter object."""
        #
        # The semantics of ParamData and parameter are different.
        # By default, ParamData are immutable.  Hence, we treat the
        # parameter objects as non-parameter data ... for now.
        #
        return False

    def is_variable_type(self):
        """A boolean indicating that this is a variable object."""
        return False

    def is_fixed(self):
        """A boolean indicating that this parameter is fixed."""
        return True

    def is_potentially_variable(self):
        """Returns :const:`False` because this object can
        never reference variables."""
        return False

    def polynomial_degree(self):
        """Always return zero because we always validate
        that the stored expression can never reference
        variables."""
        return 0
Пример #4
0
class IObjective(IExpression):
    """
    The interface for optimization objectives.
    """
    __slots__ = ()

    #
    # Implementations can choose to define these
    # properties as using __slots__, __dict__, or
    # by overriding the @property method
    #

    sense = _abstract_readwrite_property(
        doc=("The optimization direction for the "
             "objective (minimize or maximize)"))

    #
    # Interface
    #

    def is_minimizing(self):
        return self.sense == minimize
Пример #5
0
class IVariable(ICategorizedObject, NumericValue):
    """The interface for decision variables"""
    __slots__ = ()

    _valid_domain_types = (RealSet, IntegerSet)

    #
    # Implementations can choose to define these
    # properties as using __slots__, __dict__, or
    # by overriding the @property method
    #

    domain_type = _abstract_readwrite_property(
        doc=("The domain type of the variable "
             "(:class:`RealSet` or :class:`IntegerSet`)"))
    lb = _abstract_readwrite_property(doc="The lower bound of the variable")
    ub = _abstract_readwrite_property(doc="The upper bound of the variable")
    value = _abstract_readwrite_property(doc="The value of the variable")
    fixed = _abstract_readwrite_property(
        doc="The fixed status of the variable")
    stale = _abstract_readwrite_property(
        doc="The stale status of the variable")

    #
    # Interface
    #

    @property
    def bounds(self):
        """Get/Set the bounds as a tuple (lb, ub)."""
        return (self.lb, self.ub)

    @bounds.setter
    def bounds(self, bounds_tuple):
        self.lb, self.ub = bounds_tuple

    def fix(self, value=NoArgumentGiven):
        """
        Fix the variable. Sets the fixed indicator to
        :const:`True`. An optional value argument will
        update the variable's value before fixing.
        """
        if value is not NoArgumentGiven:
            self.value = value

        self.fixed = True

    def unfix(self):
        """Free the variable. Sets the fixed indicator to
        :const:`False`."""
        self.fixed = False

    free = unfix

    def has_lb(self):
        """Returns :const:`False` when the lower bound is
        :const:`None` or negative infinity"""
        lb = self.lb
        return (lb is not None) and \
            (value(lb) != _neg_inf)

    def has_ub(self):
        """Returns :const:`False` when the upper bound is
        :const:`None` or positive infinity"""
        ub = self.ub
        return (ub is not None) and \
            (value(ub) != _pos_inf)

    @property
    def lslack(self):
        """Lower slack (value - lb). Returns :const:`None` if
        the variable value is :const:`None`."""
        val = self.value
        if val is None:
            return None
        lb = self.lb
        if lb is None:
            lb = _neg_inf
        else:
            lb = value(lb)
        return val - lb

    @property
    def uslack(self):
        """Upper slack (ub - value). Returns :const:`None` if
        the variable value is :const:`None`."""
        val = self.value
        if val is None:
            return None
        ub = self.ub
        if ub is None:
            ub = _pos_inf
        else:
            ub = value(ub)
        return ub - val

    @property
    def slack(self):
        """min(lslack, uslack). Returns :const:`None` if
        the variable value is :const:`None`."""
        # this method is written so that constraint
        # types that build the body expression on the
        # fly do not have to here
        val = self.value
        if val is None:
            return None
        return min(self.lslack, self.uslack)

    #
    # Convenience methods mainly used by the solver
    # interfaces
    #

    def is_continuous(self):
        """Returns :const:`True` when the domain type is
        :class:`RealSet`."""
        return self.domain_type.get_interval()[2] == 0

    # this could be expanded to include semi-continuous
    # where as is_integer would not
    def is_discrete(self):
        """Returns :const:`True` when the domain type is
        :class:`IntegerSet`."""
        return self.domain_type.get_interval()[2] not in (0, None)

    def is_integer(self):
        """Returns :const:`True` when the domain type is
        :class:`IntegerSet`."""
        return self.domain_type.get_interval()[2] == 1

    def is_binary(self):
        """Returns :const:`True` when the domain type is
        :class:`IntegerSet` and the bounds are within
        [0,1]."""
        return self.domain_type.get_interval()[2] == 1 \
            and (value(self.lb), value(self.ub)) in {(0,1), (0,0), (1,1)}


# TODO?
#    def is_semicontinuous(self):
#        """Returns :const:`True` when the domain class is
#        SemiContinuous."""
#        return issubclass(self.domain_type, SemiRealSet)

#    def is_semiinteger(self):
#        """Returns :const:`True` when the domain class is
#        SemiInteger."""
#        return issubclass(self.domain_type, SemiIntegerSet)

#
# Implement the NumericValue abstract methods
#

    def is_fixed(self):
        """Returns :const:`True` if this variable is fixed,
        otherwise returns :const:`False`."""
        return self.fixed

    def is_constant(self):
        """Returns :const:`False` because this is not a
        constant in an expression."""
        return False

    def is_parameter_type(self):
        """Returns :const:`False` because this is not a
        parameter object."""
        return False

    def is_variable_type(self):
        """Returns :const:`True` because this is a
        variable object."""
        return True

    def is_potentially_variable(self):
        """Returns :const:`True` because this is a
        variable."""
        return True

    def polynomial_degree(self):
        """Return the polynomial degree of this
        expression"""
        # If the variable is fixed, it represents a constant;
        # otherwise, it has degree 1.
        if self.fixed:
            return 0
        return 1

    def __call__(self, exception=True):
        """Return the value of this variable."""
        if exception and (self.value is None):
            raise ValueError("value is None")
        return self.value