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)
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
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
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)