Exemple #1
0
        def __add__(self, arg):
            if isinstance(arg, PwlFunction._PwlAsBreaks):
                all_x_coord = sorted({br[0] for br in self.breaksxy + arg.breaksxy})
                all_breaks_left = self._get_all_breaks(all_x_coord)
                all_breaks_right = arg._get_all_breaks(all_x_coord)
                result_breaksxy = []
                # Both lists have same size, with same x-coord for breaks ==> perform the addition on each break
                for br_l, br_r in izip(all_breaks_left, all_breaks_right):
                    if isinstance(br_l, tuple) and isinstance(br_r, tuple):
                        result_breaksxy.append((br_l[0], br_l[1] + br_r[1]))
                    else:
                        if isinstance(br_l, tuple):
                            # br_r is a list containing 2 tuple pairs
                            result_breaksxy.append((br_l[0], br_l[1] + br_r[0][1]))
                            result_breaksxy.append((br_l[0], br_l[1] + br_r[1][1]))
                        elif isinstance(br_r, tuple):
                            # br_l is a list containing 2 tuple pairs
                            result_breaksxy.append((br_r[0], br_l[0][1] + br_r[1]))
                            result_breaksxy.append((br_r[0], br_l[1][1] + br_r[1]))
                        else:
                            # br_l and br_r are two lists, each containing 2 tuple pairs
                            result_breaksxy.append((br_l[0][0], br_l[0][1] + br_r[0][1]))
                            result_breaksxy.append((br_l[0][0], br_l[1][1] + br_r[1][1]))
                result_preslope = self.preslope + arg.preslope
                result_postslope = self.postslope + arg.postslope
                return PwlFunction._PwlAsBreaks(*self._remove_useless_intermediate_breaks(
                    result_preslope, result_breaksxy, result_postslope))

            elif is_number(arg):
                return PwlFunction._PwlAsBreaks(
                    self.preslope, [(br[0], br[1] + arg) for br in self.breaksxy], self.postslope)

            else:
                raise DOcplexException("Invalid type for right hand side operand: {0!s}.".format(arg))
Exemple #2
0
 def __mul__(self, arg):
     if is_number(arg):
         return PwlFunction._PwlAsSlopes(*self._remove_useless_intermediate_slopes(
             [(br[0] * arg, br[1]) for br in self.slopebreaksx],
             self.lastslope * arg, (self.anchor[0], self.anchor[1] * arg)))
     else:
         raise DOcplexException("Invalid type for right hand side operand: {0!s}.".format(arg))
Exemple #3
0
    def typecheck_optional_num_seq(cls,
                                   mdl,
                                   nums,
                                   accept_none=True,
                                   expected_size=None,
                                   caller=None):
        # INTERNAL
        # accepting either a num an iterable, or None (if accept_none
        if nums is None and accept_none:
            return nums
        elif is_iterable(nums, accept_string=False):
            nums_ = mdl._checker.typecheck_num_seq(nums, caller)
            lnums = list(nums_)
            if expected_size is not None:
                if len(lnums) != expected_size:
                    caller_string = '' if not caller else '%s: ' % caller
                    mdl.fatal(
                        '{0}Expecting a sequence of numbers of size {1}, actual size is {2}',
                        caller_string, expected_size, len(lnums))

            return lnums

        elif is_number(nums):
            if expected_size is not None:
                return [nums] * expected_size
            else:
                return nums
        else:
            caller_string = '' if not caller else '%s: ' % caller
            msg = "{0}Expecting a number, a sequence of numbers{1}, {2!r} was passed"
            none_msg = ' or None' if accept_none else ''
            mdl.fatal(msg, caller_string, none_msg, nums)
Exemple #4
0
 def equals(self, other):
     if is_number(other):
         return self._constant == other
     else:
         return isinstance(other, LinearOperand) \
                and other.is_constant() and \
                self._constant == other.get_constant()
Exemple #5
0
 def multiply(self, e):
     self.check_discrete_lock_frozen(e)
     if is_number(e):
         if 0 == e:
             product = self.get_linear_factory().new_zero_expr()
         else:
             self._coef *= e
             self.notify_modified(event=UpdateEvent.LinExprCoef)
             product = self
     elif isinstance(e, LinearExpr):
         product = e.times(self)
     elif isinstance(e, Var):
         product = self.model._qfactory.new_var_product(e, self)
     elif isinstance(e, MonomialExpr):
         product = self.model._qfactory.new_monomial_product(self, e)
     elif isinstance(e, Expr) and e.is_quad_expr():
         if e.has_quadratic_term():
             StaticTypeChecker.mul_quad_lin_error(self._model, self, e)
         else:
             product = self.model._qfactory.new_monomial_product(
                 self, e.linear_part)
     else:
         product = self.to_linear_expr().multiply(e)
         self.notify_replaced(product)
     return product
Exemple #6
0
 def minus(self, e):
     if isinstance(e, LinearOperand) or is_number(e):
         expr = self.to_linear_expr()
         expr.subtract(e)
         return expr
     else:
         return e.rminus(self)
Exemple #7
0
    def _sum_with_iter(self, args):
        sum_of_nums = 0
        lcc = self.counter_type()
        checker = self._checker
        qcc = None
        number_validation_fn = checker.get_number_validation_fn()
        for item in args:
            if isinstance(item, LinearOperand):
                for lv, lk in item.iter_terms():
                    update_dict_from_item_value(lcc, lv, lk)
                itc = item.get_constant()
                if itc:
                    sum_of_nums += itc
            elif is_number(item):
                sum_of_nums += number_validation_fn(item) if number_validation_fn else item
            elif isinstance(item, QuadExpr):
                for lv, lk in item.linear_part.iter_terms():
                    update_dict_from_item_value(lcc, lv, lk)
                if qcc is None:
                    qcc = self.counter_type()
                for qvp, qk in item.iter_quads():
                    update_dict_from_item_value(qcc, qvp, qk)
                sum_of_nums += item.get_constant()

            else:
                try:
                    expr = item.to_linear_expr()
                    sum_of_nums += expr.get_constant()
                    for dv, k in expr.iter_terms():
                        update_dict_from_item_value(lcc, dv, k)
                except AttributeError:
                    self._model.fatal("Model.sum() expects numbers/variables/expressions, got: {0!s}", item)

        return self._to_expr(qcc, lcc, sum_of_nums)
Exemple #8
0
    def scal_prod_triple(self, left_terms, right_terms, coefs):
        used_coefs = None
        checker = self._model._checker

        if is_iterable(coefs, accept_string=False):
            used_coefs = coefs
        elif is_number(coefs):
            if coefs:
                used_coefs = generate_constant(coefs, count_max=None)
            else:
                return self.new_zero_expr()
        else:
            self._model.fatal(
                "scal_prod_triple expects iterable or number as coefficients, got: {0!r}",
                coefs)

        if is_iterable(left_terms):
            used_left = checker.typecheck_var_seq(left_terms)
        else:
            checker.typecheck_var(left_terms)
            used_left = generate_constant(left_terms, count_max=None)

        if is_iterable(right_terms):
            used_right = checker.typecheck_var_seq(right_terms)
        else:
            checker.typecheck_var(right_terms)
            used_right = generate_constant(right_terms, count_max=None)

        if used_coefs is not coefs and used_left is not left_terms and used_right is not right_terms:
            # LOOK
            return left_terms * right_terms * coefs

        return self._scal_prod_triple(coefs=used_coefs,
                                      left_terms=used_left,
                                      right_terms=used_right)
Exemple #9
0
 def typecheck_int(cls,
                   logger,
                   arg,
                   check_math,
                   accept_negative=True,
                   caller=None):
     if not is_number(arg):
         caller_string = resolve_caller_as_string(caller)
         logger.fatal("{0}Expecting number, got: {1!r}",
                      (caller_string, arg))
     if check_math:
         if math.isnan(arg):
             caller_string = resolve_caller_as_string(caller)
             logger.fatal("{0}NaN value detected", (caller_string, ))
         elif math.isinf(arg):
             caller_string = resolve_caller_as_string(caller)
             logger.fatal("{0}Infinite value detected", (caller_string, ))
     if not is_int(arg):
         caller_string = resolve_caller_as_string(caller)
         logger.fatal("{0}Expecting integer, got: {1!r}",
                      (caller_string, arg))
     elif not accept_negative and arg < 0:
         caller_string = resolve_caller_as_string(caller)
         logger.fatal("{0}Expecting positive integer, got: {1!r}",
                      (caller_string, arg))
Exemple #10
0
 def static_validate_num2(e, infinity=1e+20, context_msg=None):
     # checks for number, nans, nath.inf, and truncates to 1e+20
     if not is_number(e):
         docplex_fatal("Not a number: {}".format(e))
     elif math.isnan(e):
         msg = "NaN value found in expression"
         if context_msg is not None:
             try:
                 msg = "{0}: {1}".format(context_msg(), msg)
             except TypeError:
                 msg = "{0}: {1}".format(context_msg, msg)
         docplex_fatal(msg)
     elif math.isinf(e):
         msg = "Infinite value forbidden in expression"
         if context_msg is not None:
             try:
                 msg = "{0}: {1}".format(context_msg(), msg)
             except TypeError:
                 msg = "{0}: {1}".format(context_msg, msg)
         docplex_fatal(msg)
     elif -infinity <= e <= infinity:
         return e
     elif e >= infinity:
         return infinity
     else:
         return -infinity
Exemple #11
0
    def subtract(self, arg):
        """ Subtracts an expression from this PWL function.

        Note:
            This method does not create a new function but modifies the `self` instance.

        Args:
            arg: The expression to be subtracted. Can be either a PWL function, or a number.

        Returns:
            The modified self.
        """
        if isinstance(arg, PwlFunction):
            if (isinstance(self.pwl_def, PwlFunction._PwlAsBreaks) and
                    isinstance(arg.pwl_def, PwlFunction._PwlAsBreaks)) or \
                    (isinstance(self.pwl_def, PwlFunction._PwlAsSlopes) and
                         isinstance(arg.pwl_def, PwlFunction._PwlAsSlopes)):
                self._pwl_def = self.pwl_def - arg.pwl_def
                self._set_pwl_definition(self._pwl_def)
            else:
                # Use Breaks representation
                self._pwl_def = self.pwl_def_as_breaks - arg.pwl_def_as_breaks
                self._set_pwl_definition(self._pwl_def)
        elif is_number(arg):
            self._pwl_def = self.pwl_def - arg
            self._set_pwl_definition(self._pwl_def)
        else:
            raise DOcplexException("Invalid type for right hand side operand: {0!s}.".format(arg))
        return self
Exemple #12
0
 def translate(self, arg):
     if is_number(arg):
         return PwlFunction._PwlAsSlopes(
             [(br[0], br[1] + arg) for br in self.slopebreaksx], self.lastslope,
             (self.anchor[0] + arg, self.anchor[1]))
     else:
         raise DOcplexException("Invalid type for argument: {0!s}.".format(arg))
    def _expand_bounds(self, keys, var_bound, default_bound, size, true_if_lb):
        ''' Converts raw bounds data (either LB or UB) to CPLEX-compatible bounds list.
            If lbs is None, this is the default, return [].
            If lbs is [] take the default again.
            If it is a number, build a list of size <size> with this number.
            If it is a list, use it if size ok (check numbers??),
            else try it as a function over keys.
        '''
        if var_bound is None:
            # default lb is zero, default ub is infinity
            return []

        elif is_number(var_bound):
            self._checker.typecheck_num(var_bound, caller='in variable bound')
            if true_if_lb:
                if var_bound == default_bound:
                    return []
                else:
                    return [float(var_bound)] * size
            else:
                # ub
                if var_bound >= default_bound:
                    return []
                else:
                    return [float(var_bound)] * size

        elif is_ordered_sequence(var_bound):
            nb_bounds = len(var_bound)
            if nb_bounds < size:
                # see how we can use defaults for those missing bounds
                self.fatal(
                    "Variable bounds list is too small, expecting: %d, got: %d"
                    % (size, nb_bounds))
            else:
                return self._check_bounds(size, var_bound, default_bound,
                                          true_if_lb)

        elif is_iterator(var_bound):
            # unfold the iterator, as CPLEX needs a list
            return list(var_bound)

        elif isinstance(var_bound, dict):
            dict_bounds = [var_bound.get(k, default_bound) for k in keys]
            return self._check_bounds(size, dict_bounds, default_bound,
                                      true_if_lb)
        else:
            # try a function?
            try:
                fn_bounds = [var_bound(k) for k in keys]
                return self._check_bounds(size, fn_bounds, default_bound,
                                          true_if_lb)

            except TypeError:
                self._bad_bounds_fatal(var_bound)

            except Exception as e:  # pragma: no cover
                self.fatal(
                    "error calling function model bounds: {0!s}, error: {1!s}",
                    var_bound, e)
Exemple #14
0
 def __sub__(self, arg):
     if isinstance(arg, PwlFunction._PwlAsSlopes):
         return self + arg * (-1)
     elif is_number(arg):
         return PwlFunction._PwlAsSlopes(copy.deepcopy(self.slopebreaksx),
                                         self.lastslope, (self.anchor[0], self.anchor[1] - arg))
     else:
         raise DOcplexException("Invalid type for right hand side operand: {0!s}.".format(arg))
Exemple #15
0
 def __sub__(self, arg):
     if isinstance(arg, PwlFunction._PwlAsBreaks):
         return self + arg * (-1)
     elif is_number(arg):
         return PwlFunction._PwlAsBreaks(
             self.preslope, [(br[0], br[1] - arg) for br in self.breaksxy], self.postslope)
     else:
         raise DOcplexException("Invalid type for right hand side operand: {0!s}.".format(arg))
Exemple #16
0
 def typecheck_int(self, arg, accept_negative=True, caller=None):
     caller_string = "{0}: ".format(caller) if caller is not None else ""
     if not is_number(arg):
         self.fatal("{0}Expecting number, got: {1!r}", caller_string, arg)
     elif not is_int(arg):
         self.fatal("{0}Expecting integer, got: {1!r}", caller_string, arg)
     elif not accept_negative and arg < 0:
         self.fatal("{0}Expecting positive integer, got: {1!r}", caller_string, arg)
Exemple #17
0
 def typecheck_as_denominator(cls, mdl, denominator, numerator):
     if not is_number(denominator):
         cls.cannot_be_used_as_denominator_error(mdl, denominator,
                                                 numerator)
     else:
         float_e = float(denominator)
         if 0 == float_e:
             mdl.fatal("Zero divide on {0!s}", numerator)
Exemple #18
0
 def _add_to_self(self, other):
     self.check_discrete_lock_frozen(item=other)
     if isinstance(other, LinearOperand) or is_number(other):
         added = self.to_linear_expr().add(other)
     else:
         added = other.plus(self)
     self.notify_replaced(added)
     return added
Exemple #19
0
 def typecheck_num(self, arg, caller=None):
     caller_string = "{0}: ".format(caller) if caller is not None else ""
     if not is_number(arg):
         self.fatal("{0}Expecting number, got: {1!r}", caller_string, arg)
     elif math.isnan(arg):
         self.fatal("{0}NaN value detected", caller_string)
     elif math.isinf(arg):
         self.fatal("{0}Infinite value detected", caller_string)
Exemple #20
0
    def __init__(self,
                 model,
                 var_value_map=None,
                 obj=None,
                 blended_obj_by_priority=None,
                 name=None,
                 solved_by=None,
                 keep_zeros=True):
        """ SolveSolution(model, var_value_map, obj, name)

        Creates a new solution object, associated to a a model.

        Args:
            model: The model to which the solution is associated. This model cannot be changed.

            obj: The value of the objective in the solution. A value of None means the objective is not defined at the
                time the solution is created, and will be set later.

            blended_obj_by_priority: For multi-objective models: the value of sub-problems' objectives (each sub-problem
                groups objectives having same priority).

            var_value_map: a Python dictionary containing associations of variables to values.

            name: a name for the solution. The default is None, in which case the solution is named after the
                model name.

        :return: A solution object.
        """
        assert model is not None
        assert solved_by is None or is_string(solved_by)
        assert obj is None or is_number(obj) or is_indexable(obj)
        assert blended_obj_by_priority is None or is_indexable(
            blended_obj_by_priority)

        self._model = model
        self._name = name
        self._problem_objective_expr = model.objective_expr if model.has_objective(
        ) else None
        self._objective = self.NO_OBJECTIVE_VALUE if obj is None else obj
        self._blended_objective_by_priority = [self.NO_OBJECTIVE_VALUE] if blended_obj_by_priority is None else \
            blended_obj_by_priority
        self._solved_by = solved_by
        self._var_value_map = {}

        # attributes
        self._reduced_costs = None
        self._dual_values = None
        self._slack_values = None
        self._infeasibilities = {}
        self._basis_statuses = None

        self._solve_status = None
        self._keep_zeros = keep_zeros
        self._solve_details = None

        if var_value_map is not None:
            self._store_var_value_map(var_value_map, keep_zeros=keep_zeros)
Exemple #21
0
 def static_validate_num(e, checked_num=False, infinity=1e+20):
     if not checked_num and not is_number(e):
         docplex_fatal("Expecting number, got: {0!r}".format(e))
     elif -infinity <= e <= infinity:
         return e
     elif e >= infinity:
         return infinity
     else:
         return -infinity
Exemple #22
0
 def typecheck_num_nan_inf(cls, logger, arg, caller=None):
     # check for a "real" number, not a NaN, not infinity
     caller_string = "{0}: ".format(caller) if caller is not None else ""
     if not is_number(arg):
         logger.fatal("{0}Expecting number, got: {1!r}", caller_string, arg)
     elif math.isnan(arg):
         logger.fatal("{0}NaN value detected", caller_string)
     elif math.isinf(arg):
         logger.fatal("{0}Infinite value detected", caller_string)
Exemple #23
0
    def scal_prod_triple_vars(self, left_terms, right_terms, coefs):
        """
        Creates a quadratic expression from two lists of variables and a sequence of coefficients.

        This method sums all quadratic terms built by multiplying the i_th coefficient by the product of the i_th
        expression in `left_terms` and the i_th expression in `right_terms`

        This method is faster than the standard generic scalar quadratic product method due to the fact that it takes only variables and does not take expressions as arguments.

        Example:
            `Model.scal_prod_vars_triple([x, y], [z, t], [2, 3])` returns the expression `2xz + 3yt`.

        :param left_terms: A list or an iterator on variables.
        :param right_terms: A list or an iterator on variables.
        :param coefs: A list or an iterator on numbers or a number.

        :returns: An instance of :class:`docplex.mp.quad.QuadExpr` or 0.

        Note:
           If either list or iterator is empty, this method returns zero.
        """
        used_coefs = None
        checker = self._checker
        nb_non_iterables = 0

        if is_iterable(coefs, accept_string=False):
            used_coefs = coefs
        elif is_number(coefs):
            if coefs:
                used_coefs = generate_constant(coefs, count_max=None)
                nb_non_iterables += 1
            else:
                return self._aggregator.new_zero_expr()
        else:
            self.fatal(
                "scal_prod_triple expects iterable or number as coefficients, got: {0!r}",
                coefs)

        if is_iterable(left_terms):
            used_left = checker.typecheck_var_seq(left_terms)
        else:
            nb_non_iterables += 1
            checker.typecheck_var(left_terms)
            used_left = generate_constant(left_terms, count_max=None)

        if is_iterable(right_terms):
            used_right = checker.typecheck_var_seq(right_terms)
        else:
            nb_non_iterables += 1
            checker.typecheck_var(right_terms)
            used_right = generate_constant(right_terms, count_max=None)

        if nb_non_iterables >= 3:
            return left_terms * right_terms * coefs
        else:
            return self._aggregator._scal_prod_triple_vars(
                left_terms=used_left, right_terms=used_right, coefs=used_coefs)
Exemple #24
0
 def translate(self, arg):
     if is_number(arg):
         return PwlFunction._PwlAsBreaks(self.preslope,
                                         [(br[0] + arg, br[1])
                                          for br in self.breaksxy],
                                         self.postslope)
     else:
         raise DOcplexException(
             "Invalid type for argument: {0!s}.".format(arg))
Exemple #25
0
    def multiply(self, e):
        """ Multiplies this expression by an expression.

        Note:
            This method does not create a new expression but modifies the `self` instance.

        Args:
            e: The expression that is used to multiply `self`.

        Returns:
            The modified `self`.

        See Also:
            The method :func:`times` to compute a multiplication without modifying the `self` instance.
        """
        mul_res = self
        event = UpdateEvent.LinExprGlobal
        self_constant = self.get_constant()
        if is_number(e):
            self._scale(factor=e)

        elif isinstance(e, LinearOperand):
            if e.is_constant():
                # simple scaling
                self._scale(factor=e.get_constant())
            elif self.is_constant():
                # self is constant: import other terms , scaled.
                # set constant to zero.
                if self_constant:
                    for lv, lk in e.iter_terms():
                        self.set_coefficient(dvar=lv, coeff=lk * self_constant)
                    self._constant *= e.get_constant()
            else:
                # yields a quadratic
                mul_res = self.model._qfactory.new_linexpr_product(self, e)
                event = UpdateEvent.LinExprPromotedToQuad

        elif isinstance(e, ZeroExpr):
            self._scale(factor=0)

        elif isinstance(e, Expr) and e.is_quad_expr():
            if not e.number_of_quadratic_terms:
                return self.multiply(e.linear_part)
            elif self.is_constant():
                return e.multiply(self.get_constant())
            else:
                StaticTypeChecker.mul_quad_lin_error(self._model, self, e)

        else:
            self.fatal(
                "Multiply expects variable, expr or number, {0!r} was passed (type is {1})",
                e, type(e))

        self.notify_modified(event=event)

        return mul_res
Exemple #26
0
    def __init__(self,
                 model,
                 e=None,
                 constant=0,
                 name=None,
                 safe=False,
                 transient=False):
        ModelingObjectBase.__init__(self, model, name)
        if not safe and constant:
            model._typecheck_num(constant, 'LinearExpr()')
        self._constant = constant
        self._transient = transient
        self._subscribers = []

        if isinstance(e, dict):
            if safe:
                self.__terms = e
            else:
                self_terms = model._lfactory.term_dict_type()
                for (v, k) in iteritems(e):
                    model._typecheck_var(v)
                    model._typecheck_num(k, 'LinearExpr')
                    if k != 0:
                        self_terms[v] = k
                self.__terms = self_terms
            return
        else:
            self.__terms = model._lfactory._new_term_dict()

        if e is None:
            pass

        elif isinstance(e, Var):
            self.__terms[e] = 1

        elif is_number(e):
            self._constant += e

        elif isinstance(e, MonomialExpr):
            # TODO: simplify by self_terms[e.var] = e.coef
            self._add_term(e.var, e.coef)

        elif isinstance(e, LinearExpr):
            # note that transient is not kept.
            self._constant = e.get_constant()
            self.__terms = self._new_terms_dict(
                model, e._get_terms_dict())  # make a copy

        elif isinstance(e, tuple):
            v, k = e
            self.__terms[v] = k

        else:
            self.fatal(
                "Cannot convert {0!r} to docplex.mp.LinearExpr, type is {1}",
                e, type(e))
Exemple #27
0
 def create_cpx_bound_list(cls, bounds, size):
     if bounds is None:
         return []
     elif is_number(bounds):
         return [bounds] * size
     elif is_ordered_sequence(bounds):
         assert size == len(bounds)
         return [float(bb) for bb in bounds]
     else:
         raise ValueError("Expecting number or sequence of numbers, {0!r} was passed".format(bounds))
Exemple #28
0
 def typecheck_num(cls, logger, arg, check_math, caller=None):
     if not is_number(arg):
         caller_string = resolve_caller_as_string(caller)
         logger.fatal("{0}Expecting number, got: {1!r}", (caller_string, arg))
     elif check_math:
         if math.isnan(arg):
             caller_string = resolve_caller_as_string(caller)
             logger.fatal("{0}NaN value detected", (caller_string,))
         elif math.isinf(arg):
             caller_string = resolve_caller_as_string(caller)
             logger.fatal("{0}Infinite value detected", (caller_string,))
Exemple #29
0
 def __mul__(self, arg):
     if is_number(arg):
         return PwlFunction._PwlAsBreaks(
             *self._remove_useless_intermediate_breaks(
                 self.preslope *
                 arg, [(br[0], br[1] * arg)
                       for br in self.breaksxy], self.postslope * arg))
     else:
         raise DOcplexException(
             "Invalid type for right hand side operand: {0!s}.".format(
                 arg))
Exemple #30
0
 def _sub_to_self(self, other):
     # INTERNAL
     self.check_discrete_lock_frozen(item=other)
     if isinstance(other, LinearOperand) or is_number(other):
         expr = self.to_linear_expr()
         expr.subtract(other)
         subtracted = expr
     else:
         subtracted = other.rminus(self)
     self.notify_replaced(subtracted)
     return subtracted