Пример #1
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)
Пример #2
0
    def equals_solution(self, other, check_models=False, check_explicit=False, obj_precision=1e-3, var_precision=1e-6):
        if check_models and (self.model != other.model):
            return False

        if is_iterable(self.objective_value) and is_iterable(other.objective_value):
            if len(self.objective_value) == len(other.objective_value):
                for self_obj_val, other_obj_val in zip(self.objective_value, other.objective_value):
                    if math.fabs(self_obj_val - other_obj_val) >= obj_precision:
                        return False
            else:  # Different number of objectives
                return False
        elif not is_iterable(self.objective_value) and not is_iterable(other.objective_value):
            if math.fabs(self.objective_value - other.objective_value) >= obj_precision:
                return False
        else:  # One solution is for multi-objective, and not the other
            return False

        for dvar, val in self.iter_var_values():
            if check_explicit and not other.contains(dvar):
                return False
            this_val = self._get_var_value(dvar)
            other_val = other._get_var_value(dvar)
            if math.fabs(this_val - other_val) >= var_precision:
                return False

        for other_dvar, other_val in other.iter_var_values():
            if check_explicit and not self.contains(other_dvar):
                return False
            this_val = self._get_var_value(other_dvar)
            other_val = other._get_var_value(other_dvar)
            if math.fabs(this_val - other_val) >= var_precision:
                return False

        return True
Пример #3
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)
Пример #4
0
 def check_list_pair_slope_breakx(logger, arg, anchor):
     if arg is None:
         logger.fatal("argument 'slopebreaksx' must be defined")
     if not is_iterable(arg):
         logger.fatal("not an iterable: {0!s}".format(arg))
     if len(arg) == 0:
         return
     if isinstance(arg, tuple):
         # Encapsulate tuple argument into a list: this allows defining a PWL with a tuple if there is only
         #  one element in its definition
         arg = [arg]
     prev_pair = None
     pprev_pair = None
     for pair in arg:
         if isinstance(pair, tuple):
             if len(pair) != 2:
                 logger.fatal("invalid tuple in 'slopebreaksx': {0!s}. Each tuple must have 2 items.".
                              format(pair))
             PwlFunction.check_number(logger, pair[0])
             PwlFunction.check_number(logger, pair[1])
         else:
             logger.fatal("invalid item in 'slopebreaksx': {0!s}. Each item must be a (x, y) tuple.".format(pair))
         if prev_pair is not None:
             if pair[1] < prev_pair[1]:
                 logger.fatal("X coordinate in: {0!s} cannot be smaller than previous break abscisse: {1!s}.".
                              format(pair, prev_pair))
             if pprev_pair is not None and pair[1] == prev_pair[1] and prev_pair[1] == pprev_pair[1]:
                 logger.fatal(
                     "invalid break: {0!s}. There cannot be more than 2 consecutive breaks with same abscisse.".
                         format(pair))
             if pair[1] == prev_pair[1] and anchor[0] == pair[1]:
                 logger.fatal("anchor {0!s} cannot be defined at discontinuity point: {1!s}".
                              format(anchor, pair))
         pprev_pair = prev_pair
         prev_pair = pair
Пример #5
0
    def make_key_seq(self, keys, name):
        # INTERNAL Takes as input a candidate keys input and returns a valid key sequence
        used_name = name
        check_keys = True
        used_keys = []
        if is_iterable(keys):
            if is_pandas_dataframe(keys):
                used_keys = keys.index.values
            elif has_len(keys):
                used_keys = keys
            elif is_iterator(keys):
                used_keys = list(keys)
            else:
                # TODO: make a test for this case.
                self.fatal(
                    "Cannot handle iterable var keys: {0!s} : no len() and not an iterator",
                    keys)  # pragma: no cover

        elif is_int(keys) and keys >= 0:
            # if name is str and we have a size, disable automatic names
            used_name = None if name is str else name
            used_keys = range(keys)
            check_keys = False
        else:
            self.fatal(
                "Unexpected var keys: {0!s}, expecting iterable or integer",
                keys)  # pragma: no cover

        if check_keys and len(
                used_keys
        ):  # do not check truth value of used_keys: can be a Series!
            self._checker.typecheck_key_seq(used_keys)
        return used_name, used_keys
Пример #6
0
    def _lazy_compute_name_string(self):
        if self._name_str is not None:
            return self._name_str
        else:
            raw_name = self._name
            if is_string(raw_name):
                # drop opl-style formats
                s_name = raw_name.replace("({%s})", "")
                # purge fields
                pos_pct = raw_name.find('%')
                if pos_pct >= 0:
                    s_name = raw_name[:pos_pct - 1]
                elif raw_name.find('{') > 0:
                    pos = raw_name.find('{')
                    s_name = raw_name[:pos - 1]
            elif is_iterable(raw_name):
                from os.path import commonprefix
                s_name = commonprefix(raw_name)
            else:
                # try a function
                from os.path import commonprefix
                namefn = raw_name
                try:
                    all_names = [namefn(k) for k in self.iter_keys()]
                    s_name = commonprefix(all_names)

                except TypeError:
                    s_name = ''

            self._name_str = s_name
            return s_name
Пример #7
0
 def _print_to_stream2(cls,
                       out,
                       solutions,
                       write_level,
                       use_lp_names,
                       effort_level=None):
     # no kwargs at this stage.
     # solutions can be either a plain solution or a sequence or an iterator
     if not is_iterable(solutions):
         cls.print_one_solution(solutions,
                                out,
                                use_lp_names=use_lp_names,
                                write_level=write_level,
                                effort_level=effort_level)
     else:
         sol_seq = list(solutions)
         nb_solutions = len(sol_seq)
         assert nb_solutions > 0
         if 1 == nb_solutions:
             cls.print_one_solution(sol_seq[0],
                                    out,
                                    use_lp_names,
                                    write_level=write_level,
                                    effort_level=effort_level[0])
         else:
             cls.print_many_solutions(sol_seq, out, use_lp_names,
                                      write_level, effort_level)
Пример #8
0
    def print_many_solutions(cls,
                             sol_seq,
                             out,
                             use_lp_names,
                             write_level,
                             effort_level=None):
        osa = OutputStreamAdapter(out)
        osa.write(cls.mst_header)
        cls.print_signature(out, write_level)
        # <CPLEXSolutions version="1.0">
        osa.write(cls.many_solution_start_tag)
        osa.write("\n")

        efforts = [EffortLevel.Auto]
        if effort_level is None:
            pass
        elif is_iterable(effort_level):
            efforts = effort_level
        else:
            efforts = [effort_level]

        for sol, effort in izip_longest(sol_seq,
                                        efforts,
                                        fillvalue=EffortLevel.Auto):
            cls.print_one_solution(sol,
                                   out,
                                   print_header=False,
                                   use_lp_names=use_lp_names,
                                   write_level=write_level,
                                   effort_level=effort)

        # <CPLEXSolutions version="1.0">
        osa.write(cls.many_solution_end_tag)
        osa.write("\n")
Пример #9
0
 def check_list_pair_breaksxy(logger, arg):
     if not is_iterable(arg):
         logger.fatal("argument 'breaksxy' expects iterable, {0!r} was passed".format(arg))
     if isinstance(arg, tuple):
         # Encapsulate tuple argument into a list: this allows defining a PWL with a tuple if there is only
         #  one element in its definition
         arg = [arg]
     if len(arg) == 0:
         logger.fatal("argument 'breaksxy' must be a non-empty list of (x, y) tuples.")
     prev_pair = None
     pprev_pair = None
     for pair in arg:
         if isinstance(pair, tuple):
             if len(pair) != 2:
                 logger.fatal("invalid tuple in 'breaksxy': {0!s}. Each tuple must have 2 items.".format(pair))
             PwlFunction.check_number(logger, pair[0])
             PwlFunction.check_number(logger, pair[1])
         else:
             logger.fatal("invalid item in 'breaksxy': {0!s}. Each item must be a (x, y) tuple.".format(pair))
         if prev_pair is not None:
             if pair[0] < prev_pair[0]:
                 logger.fatal("X coordinate in: {0!s} cannot be smaller than previous break abscisse: {1!s}.".
                              format(pair, prev_pair))
             if pprev_pair is not None and pair[0] == prev_pair[0] and prev_pair[0] == pprev_pair[0]:
                 logger.fatal(
                     "invalid break: {0!s}. There cannot be more than 2 consecutive breaks with same abscisse.".
                         format(pair))
         pprev_pair = prev_pair
         prev_pair = pair
Пример #10
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)
Пример #11
0
 def __init__(self, model, exprs, name=None):
     _IAdvancedExpr.__init__(self, model, name)
     if is_iterable(exprs) or is_iterator(exprs):
         self._exprs = exprs
     else:
         self._exprs = [model._lfactory._to_linear_operand(exprs)]
     # allocate xvars iff necessary
     self._xvars = [self._allocate_arg_var_if_necessary(e) for e in self._exprs]
Пример #12
0
 def add_new_pattern(self, item_usages):
     """ makes a new pattern from a sequence of usages (one per item)"""
     assert is_iterable(item_usages)
     new_pattern_id = self.max_pattern_id + 1
     new_pattern = TPattern(new_pattern_id, 1)
     self.patterns.append(new_pattern)
     self.max_pattern_id = new_pattern_id
     for used, item in zip(item_usages, self.items):
         self.pattern_item_filled[new_pattern, item] = used
Пример #13
0
    def get_attribute(self, mobjs, attr, default_attr_value=0):
        assert is_iterable(mobjs)

        if attr not in self._attribute_map:
            # warn
            return [0] * len(mobjs)
        else:
            attr_map = self._attribute_map[attr]
            return [attr_map.get(mobj, default_attr_value) for mobj in mobjs]
Пример #14
0
 def add_new_pattern(self, item_usages):
     """ makes a new pattern from a sequence of usages (one per item)"""
     assert is_iterable(item_usages)
     new_pattern_id = self.max_pattern_id + 1
     new_pattern = TPattern(new_pattern_id, 1)
     self.patterns.append(new_pattern)
     self.max_pattern_id = new_pattern_id
     for used, item in zip(item_usages, self.items):
         self.pattern_item_filled[new_pattern, item] = used
Пример #15
0
    def equals(self,
               other,
               check_models=False,
               obj_precision=1e-3,
               var_precision=1e-6):
        from itertools import dropwhile
        if check_models and (self.model != other.model):
            return False

        if is_iterable(self.objective_value) and is_iterable(
                other.objective_value):
            if len(self.objective_value) == len(other.objective_value):
                for self_obj_val, other_obj_val in zip(self.objective_value,
                                                       other.objective_value):
                    if abs(self_obj_val - other_obj_val) >= obj_precision:
                        return False
            else:  # Different number of objectives
                return False
        elif not is_iterable(self.objective_value) and not is_iterable(
                other.objective_value):
            if abs(self.objective_value -
                   other.objective_value) >= obj_precision:
                return False
        else:  # One solution is for multi-objective, and not the other
            return False

        # noinspection PyPep8
        this_triplets = [(dv.index, dv.name, svalue)
                         for dv, svalue in dropwhile(lambda dvv: not dvv[1],
                                                     self.iter_var_values())]
        other_triplets = [(dv.index, dv.name, svalue)
                          for dv, svalue in dropwhile(lambda dvv: not dvv[1],
                                                      other.iter_var_values())]
        # noinspection PyArgumentList
        res = True
        for this_triple, other_triple in izip(this_triplets, other_triplets):
            this_index, this_name, this_val = this_triple
            other_index, other_name, other_val = other_triple
            if other_index != this_index or this_name != other_name or \
                    abs(this_val - other_val) >= var_precision:
                res = False
                break
        return res
Пример #16
0
 def add_new_pattern(self, item_usages):
     """ makes a new pattern from a sequence of usages (one per item)"""
     assert is_iterable(item_usages)
     new_pattern_id = self.max_pattern_id + 1
     new_pattern = TPattern(new_pattern_id, 1)
     self.patterns.append(new_pattern)
     self.max_pattern_id = new_pattern_id
     for i in range(len(item_usages)):
         used = item_usages[i]
         item = self.items[i]
         self.pattern_item_filled[new_pattern, item] = used
 def __init__(self, model, exprs, name=None):
     _FunctionalExpr.__init__(self, model, name)
     if is_iterable(exprs) or is_iterator(exprs):
         self._exprs = exprs
     else:
         self._exprs = [model._lfactory._to_linear_operand(exprs)]
     # allocate xvars iff necessary
     self._xvars = [
         self._allocate_arg_var_if_necessary(expr, pos=e)
         for e, expr in enumerate(self._exprs, start=1)
     ]
Пример #18
0
 def print(cls, out, solutions):
     # solutions can be either a plain solution or a sequence or an iterator
     if not is_iterable(solutions):
         cls.print_one_solution(solutions, out)
     else:
         sol_seq = list(solutions)
         nb_solutions = len(sol_seq)
         assert nb_solutions > 0
         if 1 == nb_solutions:
             cls.print_one_solution(sol_seq[0], out)
         else:
             cls.print_many_solutions(sol_seq, out)
Пример #19
0
def compile_naming_function(keys,
                            user_name,
                            dimension=1,
                            key_format=None,
                            _default_key_format='_%s',
                            stringifier=str_flatten_tuple):
    # INTERNAL
    # builds a naming rule from an input , a dimension, and an optional meta-format
    # Makes sure the format string does contain the right number of format slots
    assert user_name is not None

    if is_string(user_name):
        if key_format is None:
            used_key_format = _default_key_format
        elif is_string(key_format):
            # -- make sure some %s is inside, otherwise add it
            if '%s' in key_format:
                used_key_format = key_format
            else:
                used_key_format = key_format + '%s'
        else:  # pragma: no cover
            raise DOcplexException(
                "key format expects string format or None, got: {0!r}".format(
                    key_format))

        fixed_format_string = fix_format_string(user_name, dimension,
                                                used_key_format)
        if 1 == dimension:
            return lambda k: fixed_format_string % stringifier(k)
        else:
            # here keys are tuples of size >= 2
            return lambda key_tuple: fixed_format_string % key_tuple

    elif is_function(user_name):
        return user_name

    elif is_iterable(user_name):
        # check that the iterable has same len as keys,
        # otherwise thereis no more default naming and None cannot appear in CPLEX name arrays
        list_names = list(user_name)
        if len(list_names) < len(keys):
            raise DOcplexException(
                "An array of names should have same len as keys, expecting: {0}, go: {1}"
                .format(len(keys), len(list_names)))
        key_to_names_dict = {k: nm for k, nm in izip(keys, list_names)}
        # use a closure
        return lambda k: key_to_names_dict[
            k]  # if k in key_to_names_dict else default_fn()

    else:
        raise DOcplexException(
            'Cannot use this for naming variables: {0!r} - expecting string, function or iterable'
            .format(user_name))
Пример #20
0
 def print_to_stream2(cls, out, solutions, indent=None, **kwargs):
     # solutions can be either a plain solution or a sequence or an iterator
     sol_to_print = list(solutions) if is_iterable(solutions) else [solutions]
     # encode all solutions in dict ready for json output
     encoder = SolutionJSONEncoder(**kwargs)
     solutions_as_dict = [encoder.default(sol) for sol in sol_to_print]
     # use an output stream adapter for py2/py3 and str/unicode compatibility
     osa = OutputStreamAdapter(out)
     if len(sol_to_print) == 1:  # if only one solution, use at root node
         osa.write(json.dumps(solutions_as_dict[0], indent=indent))
     else:  # for multiple solutions, we want a "CPLEXSolutions" root
         osa.write(json.dumps({"CPLEXSolutions": solutions_as_dict}, indent=indent))
Пример #21
0
def docplex_sum(x_seq):
    if is_iterable(x_seq):
        if not x_seq:
            return 0
        elif isinstance(x_seq, dict):
            return _docplex_sum_with_seq(x_seq.values())
        elif is_indexable(x_seq):
            return _docplex_sum_with_seq(x_seq)
        else:
            return _docplex_sum_with_seq(list(x_seq))
    elif x_seq:
        mdl = _docplex_extract_model(x_seq, do_raise=False)
        return mdl.to_linear_expr(x_seq) if mdl else x_seq
    else:
        return 0
Пример #22
0
 def _sum_vars(self, dvars):
     if is_numpy_ndarray(dvars):
         return self._sum_vars(dvars.flat)
     elif is_pandas_series(dvars):
         return self.sum(dvars.values)
     elif isinstance(dvars, dict):
         # handle dict: sum all values
         return self._sum_vars(itervalues(dvars))
     elif is_iterable(dvars):
         checked_dvars = self._checker.typecheck_var_seq(dvars, caller='Model.sumvars()')
         sumvars_terms = self._varlist_to_terms(checked_dvars)
         return self._to_expr(qcc=None, lcc=sumvars_terms)
     else:
         self._model.fatal('Model.sumvars() expects an iterable returning variables, {0!r} was passed',
                           dvars)
Пример #23
0
    def sumsq(self, sum_args):
        if is_iterable(sum_args):
            if is_iterator(sum_args):
                return self._sumsq(sum_args)
            elif isinstance(sum_args, dict):
                return self._sumsq(sum_args.values())
            elif is_numpy_ndarray(sum_args):
                return self._sumsq(sum_args.flat)
            elif is_pandas_series(sum_args):
                return self._sumsq(sum_args.values)

            else:
                return self._sumsq(sum_args)
        elif is_number(sum_args):
            return sum_args ** 2
        else:
            self._model.fatal("Model.sumsq() expects number/iterable/expression, got: {0!s}", sum_args)
Пример #24
0
    def sum(self, sum_args):
        if is_iterator(sum_args):
            return self._sum_with_iter(sum_args)

        elif is_numpy_ndarray(sum_args):
            return self._sum_with_iter(sum_args.flat)
        elif is_pandas_series(sum_args):
            return self.sum(sum_args.values)
        elif isinstance(sum_args, dict):
            # handle dict: sum all values
            return self._sum_with_iter(itervalues(sum_args))
        elif is_iterable(sum_args):
            return self._sum_with_seq(sum_args)

        elif is_number(sum_args):
            return sum_args
        else:
            return self._linear_factory._to_linear_expr(sum_args)
Пример #25
0
    def scal_prod(self, terms, coefs=1.0):
        # Testing anumpy array for its logical value will not work:
        # ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
        # we would have to trap the test for ValueError then call any()
        #
        if is_iterable(coefs):
            pass  # ok
        elif is_number(coefs):
            if 0 == coefs:
                return self.new_zero_expr()
            else:
                sum_expr = self.sum(terms)
                return sum_expr * coefs
        else:
            self._model.fatal("scal_prod expects iterable or number, {0!s} was passed", coefs)

        # model has checked terms is an ordered sequence
        return self._scal_prod(terms, coefs)
Пример #26
0
    def _scal_prod_vars_all_different(self, terms, coefs):
        checker = self._checker
        if not is_iterable(coefs, accept_string=False):
            checker.typecheck_num(coefs)
            return coefs * self._sum_vars_all_different(terms)
        else:
            # coefs is iterable
            lcc_type = self.counter_type
            lcc = lcc_type()
            lcc_setitem = lcc_type.__setitem__
            number_validation_fn = checker.get_number_validation_fn()
            if number_validation_fn:
                for dvar, coef in izip(terms, coefs):
                    lcc_setitem(lcc, dvar, number_validation_fn(coef))
            else:
                for dvar, coef in izip(terms, coefs):
                    lcc_setitem(lcc, dvar, coef)

            return self._to_expr(qcc=None, lcc=lcc)
Пример #27
0
 def __init__(self, model, exprs, name=None):
     _FunctionalExpr.__init__(self, model, name)
     assert is_iterable(exprs) or is_iterator(exprs)
     self._exprs = exprs
     # never allocate vars: arguments --are-- binary variables.
     self._xvars = exprs
Пример #28
0
 def typecheck_iterable(self, arg):
     # INTERNAL: checks for an iterable
     if not is_iterable(arg):
         self.fatal("Expecting iterable, got: {0!s}", arg)