Source code for docplex.mp.vartype

# --------------------------------------------------------------------------
# Source file provided under Apache License, Version 2.0, January 2004,
# http://www.apache.org/licenses/
# (c) Copyright IBM Corp. 2015, 2016
# --------------------------------------------------------------------------

from docplex.mp.error_handler import docplex_fatal

[docs]class VarType(object): """VarType() This abstract class is the parent class for all types of decision variables. This class must never be instantiated. Specialized sub-classes are defined for each type of decision variable. """ def __init__(self, short_name, lb, ub, cplex_typecode): self._short_name = short_name self._lb = lb self._ub = ub self._cpx_typecode = cplex_typecode def is_semi_type(self): return False @property def cplex_typecode(self): """ This property returns the CPLEX type code for this type. Possible values are: - 'B' for binary type - 'I' for integer type - 'C' for continuous type - 'S' for semicontinuous type - 'N' for semiinteger type :return: a one-letter string. """ return self._cpx_typecode @property def short_name(self): """ This property returns a short name string for the type. """ return self._short_name @property def default_lb(self): """ This property returns the default lower bound for the type. """ return self._lb @property def default_ub(self): """ This property returns the default upper bound for the type. """ return self._ub def resolve_lb(self, candidate_lb, logger): if candidate_lb is None: resolved_lb = self._lb else: resolved_lb = self._compute_lb(candidate_lb, logger) return resolved_lb def resolve_ub(self, candidate_ub, logger): if candidate_ub is None: resolved_ub = self._ub else: resolved_ub = self._compute_ub(candidate_ub, logger) return resolved_ub def _compute_lb(self, candidate_lb, logger): # pragma: no cover # INTERNAL raise NotImplementedError def _compute_ub(self, candidate_ub, logger): # pragma: no cover # INTERNAL raise NotImplementedError
[docs] def is_discrete(self): """ Checks if this is a discrete type. Returns: Boolean: True if the type is a discrete type. """ raise NotImplementedError # pragma: no cover
def accept_value(self, numeric_value, tolerance=1e-6): raise NotImplementedError # pragma: no cover @classmethod def _is_within_bounds_and_tolerance(cls, candidate_value, lb, ub, tolerance): assert tolerance >= 0 if candidate_value < lb - tolerance: res = False elif candidate_value > ub + tolerance: res = False else: res = True return res @classmethod def _is_int_within_tolerance(cls, candidate_value, tolerance): assert tolerance >= 0 return abs(candidate_value - round(candidate_value)) <= tolerance def accept_domain_value(self, candidate_value, lb, ub, tolerance): return self.accept_value(candidate_value, tolerance) and\ self._is_within_bounds_and_tolerance(candidate_value, lb, ub, tolerance)
[docs] def to_string(self): """ Returns: string: A string representation of the type. """ return "VarType_%s" % self.short_name
def __str__(self): return self.to_string() def __eq__(self, other): return type(other) == type(self) def __ne__(self, other): return type(other) != type(self) def _hash_vartype(self): # pragma: no cover return hash(self.cplex_typecode)
[docs]class BinaryVarType(VarType): """BinaryVarType() This class models the binary variable type and is not meant to be instantiated. Each model contains one instance of this type. """ def __init__(self): VarType.__init__(self, short_name="binary", lb=0, ub=1, cplex_typecode='B') def _compute_lb(self, candidate_lb, logger): # INTERNAL if candidate_lb >= 1 + 1e-6: logger.fatal('Lower bound for binary variable should be less than 1, {0} was passed '.format(candidate_lb)) # return the user bound anyway return candidate_lb def _compute_ub(self, candidate_ub, logger): # INTERNAL if candidate_ub <= -1e-6: logger.fatal('Upper bound for binary variable should be greater than 0, {0} was passed'.format(candidate_ub)) # return the user bound anyway return candidate_ub
[docs] def is_discrete(self): """ Checks if this is a discrete type. Returns: Boolean: True as this is a discrete type. """ return True
def accept_value(self, numeric_value, tolerance=1e-6): return -tolerance <= numeric_value <= tolerance or\ (1-tolerance <= numeric_value <= 1 + tolerance) def __hash__(self): # pragma: no cover return VarType._hash_vartype(self)
[docs]class ContinuousVarType(VarType): """ContinuousVarType() This class models the continuous variable type and is not meant to be instantiated. Each model contains one instance of this type. """ def __init__(self, plus_infinity=1e+20): VarType.__init__(self, short_name="continuous", lb=0, ub=plus_infinity, cplex_typecode='C') self._plus_infinity = plus_infinity self._minus_infinity = - plus_infinity def _compute_ub(self, candidate_ub, logger): return min(candidate_ub, self._plus_infinity) def _compute_lb(self, candidate_lb, logger): return max(candidate_lb, self._minus_infinity)
[docs] def is_discrete(self): """ Checks if this is a discrete type. Returns: Boolean: False because this type is not a discrete type. """ return False
def accept_value(self, numeric_value, tolerance=1e-6): return self._minus_infinity <= numeric_value <= self._plus_infinity def __hash__(self): # pragma: no cover return VarType._hash_vartype(self)
[docs]class IntegerVarType(VarType): """IntegerVarType() This class models the integer variable type and is not meant to be instantiated. Each models contains one instance of this type. """ def __init__(self, plus_infinity=1e+20): VarType.__init__(self, short_name="integer", lb=0, ub=plus_infinity, cplex_typecode='I') self._plus_infinity = plus_infinity self._minus_infinity = -plus_infinity def _compute_ub(self, candidate_ub, logger): return min(candidate_ub, self._plus_infinity) def _compute_lb(self, candidate_lb, logger): return max(candidate_lb, self._minus_infinity)
[docs] def is_discrete(self): """ Checks if this is a discrete type. Returns: Boolean: True as this is a discrete type. """ return True
def accept_value(self, numeric_value, tolerance=1e-6): return self._is_int_within_tolerance(numeric_value, tolerance) def __hash__(self): # pragma: no cover return VarType._hash_vartype(self)
[docs]class SemiContinuousVarType(VarType): """SemiContinuousVarType() This class models the :index:`semi-continuous` variable type and is not meant to be instantiated. """ def __init__(self, plus_infinity=1e+20): VarType.__init__(self, short_name="semi-continuous", lb=1e-6, ub=plus_infinity, cplex_typecode='S') self._plus_infinity = plus_infinity def is_semi_type(self): return True def _compute_ub(self, candidate_ub, logger): return self._plus_infinity if candidate_ub >= self._plus_infinity else float(candidate_ub) def _compute_lb(self, candidate_lb, logger): if candidate_lb <= 0: logger.fatal( 'semi-continuous variable expects strict positive lower bound, not: {0}'.format(candidate_lb)) return candidate_lb @property def default_lb(self): # there is NO default lb docplex_fatal("Type {0} has no default lower bound".format(self.short_name))
[docs] def is_discrete(self): """ Checks if this is a discrete type. Returns: Boolean: False because this type is not a discrete type. """ return False
def accept_value(self, numeric_value, tolerance=1e-6): return 0 <= numeric_value <= self._plus_infinity def accept_domain_value(self, candidate_value, lb, ub, tolerance): return 0 == candidate_value or self._is_within_bounds_and_tolerance(candidate_value, lb, ub, tolerance) def __hash__(self): # pragma: no cover return VarType._hash_vartype(self)
[docs]class SemiIntegerVarType(VarType): """SemiIntegerVarType() This class models the :index:`semi-integer` variable type and is not meant to be instantiated. """ def __init__(self, plus_infinity=1e+20): VarType.__init__(self, short_name="semi-integer", lb=1e-6, ub=plus_infinity, cplex_typecode='N') self._plus_infinity = plus_infinity def is_semi_type(self): return True @property def default_lb(self): # there is NO default lb docplex_fatal("Type {0} has no default lower bound".format(self.short_name)) def _compute_ub(self, candidate_ub, logger): return min(candidate_ub, self._plus_infinity) def _compute_lb(self, candidate_lb, logger): if candidate_lb <= 0: logger.fatal('semi-integer variable expects strict positive lower bound, not: {0}'.format(candidate_lb)) return candidate_lb
[docs] def is_discrete(self): """ Checks if this is a discrete type. Returns: Boolean: True because this type is an integer type. """ return True
def accept_value(self, numeric_value, tolerance=1e-6): if 0 == numeric_value: return True return numeric_value >= 0 and self._is_int_within_tolerance(numeric_value, tolerance) def accept_domain_value(self, candidate_value, lb, ub, tolerance): return 0 == candidate_value or self._is_within_bounds_and_tolerance(candidate_value, lb, ub, tolerance) def __hash__(self): # pragma: no cover return VarType._hash_vartype(self)