예제 #1
0
def _parse_declaration(solver: pywraplp.Solver, core_line: str, line_nr: int,
                       var_names: set):
    spl_whitespace = core_line.split(maxsplit=1)

    if spl_whitespace[0] != "int":
        raise ValueError("Declaration on line %d should start with \"int \"." %
                         line_nr)

    if len(spl_whitespace) != 2:
        raise ValueError("Declaration on line %d has no variables." % line_nr)

    spl_variables = spl_whitespace[1].split(",")
    for raw_var in spl_variables:
        clean_var = raw_var.strip()
        if not re.match(_REGEXP_SINGLE_VAR_NAME_ALL, clean_var):
            raise ValueError(
                "Non-permitted variable name (\"%s\") on line %d." %
                (clean_var, line_nr))
        if clean_var in var_names:
            raise ValueError("Variable \"%s\" declared again on line %d." %
                             (clean_var, line_nr))
        var_names.add(clean_var)
        solver.IntVar(-solver.infinity(), solver.infinity(), clean_var)
예제 #2
0
    def _add_stock_variables(num_types: int, num_time_periods: int, solver: Solver) \
            -> Dict[V, Variable]:
        """Create stock variables.

         A stock variable $s^t_p$ is a non-negative real variable which represents the number of
         items of type $t$ on stock in time period $p$.

        :param num_types: the number of considered types
        :param num_time_periods: the number of considered time periods
        :param solver: the underlying solver for which to built the variables
        :return: A dictionary mapping each stock variable to its solver variable
        """
        stock_vars = dict()
        for (item_type, time_period) in product(range(num_types),
                                                range(-1, num_time_periods)):
            stock_vars[V(type=item_type, period=time_period)] = \
                solver.NumVar(lb=0., ub=solver.infinity(), name=f's_{item_type}_{time_period}')
        module_logger.info(f'Created {len(stock_vars)} stock variables.')
        return stock_vars
예제 #3
0
def _set_coefficients(solver: pywraplp.Solver, objective_or_constraint,
                      coefficient_part: str, line_nr: int, var_names: set):

    # Strip the coefficient whitespace
    remainder = coefficient_part.strip()
    if len(remainder) == 0:
        raise ValueError("No variables present in equation on line %d." %
                         line_nr)

    # All variables found
    var_names_found = set()

    running_constant_sum = 0.0
    had_at_least_one_variable = False
    while len(remainder) != 0:

        # Combination sign
        coefficient = 1.0
        combination_sign_match = re.search(r"^[-+]", remainder)
        if combination_sign_match is not None:
            if combination_sign_match.group() == "-":
                coefficient = -1.0
            remainder = remainder[1:].strip()

        # Real sign
        sign_match = re.search(r"^[-+]", remainder)
        if sign_match is not None:
            if sign_match.group() == "-":
                coefficient = coefficient * -1.0
            remainder = remainder[
                1:]  # There is no strip() here, as it must be directly in front of the mantissa

        # Mantissa and exponent
        mantissa_exp_match = re.search(r"^(\d+(\.\d*)?|\.\d+)([eE][-+]?\d+)?",
                                       remainder)
        whitespace_after = False
        if mantissa_exp_match is not None:
            coefficient = coefficient * float(mantissa_exp_match.group())
            remainder = remainder[mantissa_exp_match.span()[1]:]
            stripped_remainder = remainder.strip()
            if len(remainder) != len(stripped_remainder):
                whitespace_after = True
            remainder = stripped_remainder

        # Variable name
        var_name_match = re.search(_REGEXP_SINGLE_VAR_NAME_START, remainder)
        if var_name_match is not None:

            # It must have had at least one variable
            had_at_least_one_variable = True

            # Retrieve clean variable name
            clean_var = var_name_match.group()
            var_names.add(clean_var)
            if clean_var in var_names_found:
                raise ValueError(
                    "Variable \"%s\" found more than once on line %d." %
                    (clean_var, line_nr))
            var_names_found.add(clean_var)
            solver_var = solver.LookupVariable(clean_var)
            if solver_var is None:
                solver_var = solver.NumVar(-solver.infinity(),
                                           solver.infinity(), clean_var)

            # Set coefficient
            objective_or_constraint.SetCoefficient(solver_var, coefficient)

            # Strip what we matched
            remainder = remainder[var_name_match.span()[1]:]
            stripped_remainder = remainder.strip()
            whitespace_after = False
            if len(remainder) != len(stripped_remainder):
                whitespace_after = True
            remainder = stripped_remainder

        elif mantissa_exp_match is None:
            raise ValueError(
                "Cannot process remainder coefficients of \"%s\" on line %d." %
                (remainder, line_nr))

        else:
            running_constant_sum += coefficient

        # At the end of each element there either:
        # (a) Must be whitespace (e.g., x1 x2 <= 10)
        # (b) The next combination sign (e.g., x1+x2 <= 10)
        # (c) Or it was the last one, as such remainder is empty (e.g., x1 <= 10)
        if len(remainder) != 0 and not whitespace_after and remainder[
                0:1] != "-" and remainder[0:1] != "+":
            raise ValueError(
                "Unexpected next character \"%s\" on line %d (expected whitespace or "
                "combination sign character)." % (remainder[0:1], line_nr))

    # There must have been at least one variable
    if not had_at_least_one_variable:
        raise ValueError(
            "Not a single variable present in the coefficients on line %d." %
            line_nr)

    return running_constant_sum
예제 #4
0
def _parse_constraint(solver: pywraplp.Solver, core_line: str, line_nr: int,
                      var_names: set):

    # We don't care about the coefficient name before the colon
    constraint_part = core_line
    spl_colon = core_line.split(":", maxsplit=1)
    if len(spl_colon) > 1:
        constraint_part = spl_colon[1].strip()

    # Equality constraint
    if constraint_part.find("=") >= 0 and constraint_part.find(
            "<=") == -1 and constraint_part.find(">=") == -1:
        equality_spl = constraint_part.split("=")
        if len(equality_spl) > 2:
            raise ValueError(
                "Equality constraint on line %d has multiple equal signs." %
                line_nr)
        if not _is_valid_constant_float(equality_spl[1]):
            raise ValueError(
                "Right hand side (\"%s\") of equality constraint on line %d is not a float "
                "(e.g., variables are not allowed there!)." %
                (equality_spl[1], line_nr))
        equal_value = float(equality_spl[1])
        constraint = solver.Constraint(equal_value, equal_value)
        constant = _set_coefficients(solver, constraint, equality_spl[0],
                                     line_nr, var_names)
        constraint.SetLb(constraint.Lb() - constant)
        constraint.SetUb(constraint.Ub() - constant)
        _attempt_to_improve_var_bounds_two_hs(solver, equality_spl[0], True,
                                              equality_spl[1], equality_spl[1])

    # Inequality constraints
    else:

        # Replace all of these inequality signs, because they are equivalent
        constraint_part = constraint_part.replace("<=", "<").replace(">=", ">")

        # lower bound < ... < upper bound
        if constraint_part.count("<") == 2:
            spl = constraint_part.split("<")
            if not _is_valid_constant_float(spl[0]):
                raise ValueError(
                    "Left hand side (\"%s\") of inequality constraint on line %d is not a float "
                    "(e.g., variables are not allowed there!)." %
                    (spl[0], line_nr))
            if not _is_valid_constant_float(spl[2]):
                raise ValueError(
                    "Right hand side (\"%s\") of inequality constraint on line %d is not a float "
                    "(e.g., variables are not allowed there!)." %
                    (spl[2], line_nr))
            constraint = solver.Constraint(float(spl[0]), float(spl[2]))
            constant = _set_coefficients(solver, constraint, spl[1], line_nr,
                                         var_names)
            constraint.SetLb(constraint.Lb() - constant)
            constraint.SetUb(constraint.Ub() - constant)
            _attempt_to_improve_var_bounds_two_hs(solver, spl[1], True, spl[0],
                                                  spl[2])

        # upper bound > ... > lower bound
        elif constraint_part.count(">") == 2:
            spl = constraint_part.split(">")
            if not _is_valid_constant_float(spl[0]):
                raise ValueError(
                    "Left hand side (\"%s\") of inequality constraint on line %d is not a float "
                    "(e.g., variables are not allowed there!)." %
                    (spl[0], line_nr))
            if not _is_valid_constant_float(spl[2]):
                raise ValueError(
                    "Right hand side (\"%s\") of inequality constraint on line %d is not a float "
                    "(e.g., variables are not allowed there!)." %
                    (spl[2], line_nr))
            constraint = solver.Constraint(float(spl[2]), float(spl[0]))
            constant = _set_coefficients(solver, constraint, spl[1], line_nr,
                                         var_names)
            constraint.SetLb(constraint.Lb() - constant)
            constraint.SetUb(constraint.Ub() - constant)
            _attempt_to_improve_var_bounds_two_hs(solver, spl[1], False,
                                                  spl[0], spl[2])

        # ... < upper bound
        elif constraint_part.count("<") == 1:
            spl = constraint_part.split("<")
            if not _is_valid_constant_float(spl[1]):
                raise ValueError(
                    "Right hand side (\"%s\") of inequality constraint on line %d is not a float "
                    "(e.g., variables are not allowed there!)." %
                    (spl[1], line_nr))
            constraint = solver.Constraint(-solver.infinity(), float(spl[1]))
            constant = _set_coefficients(solver, constraint, spl[0], line_nr,
                                         var_names)
            constraint.SetUb(constraint.Ub() - constant)
            _attempt_to_improve_var_bounds_one_hs(solver, spl[0], True, spl[1])

        # ... > lower bound
        elif constraint_part.count(">") == 1:
            spl = constraint_part.split(">")
            if not _is_valid_constant_float(spl[1]):
                raise ValueError(
                    "Right hand side (\"%s\") of inequality constraint on line %d is not a float "
                    "(e.g., variables are not allowed there!)." %
                    (spl[1], line_nr))
            constraint = solver.Constraint(float(spl[1]), solver.infinity())
            constant = _set_coefficients(solver, constraint, spl[0], line_nr,
                                         var_names)
            constraint.SetLb(constraint.Lb() - constant)
            _attempt_to_improve_var_bounds_one_hs(solver, spl[0], False,
                                                  spl[1])

        # ...
        elif constraint_part.count(">") == 0 and constraint_part.count(
                "<") == 0:
            raise ValueError(
                "No (in)equality sign present for constraint on line %d." %
                line_nr)

        # Some strange combination
        else:
            raise ValueError(
                "Too many (in)equality signs present for constraint on line %d."
                % line_nr)