def find_transform(x, y, limit, threshold=1e-7): """ find a integer solution to ax + b - cxy - dy = 0 this will give us the mobius transform: T(x) = y :param x: numeric constant to check :param y: numeric manipulation of constant :param limit: range to look at :param threshold: optimal solution threshold. :return MobiusTransform in case of success or None. """ x1 = x x2 = dec(1.0) x3 = -x * y x4 = -y solver = Solver('mobius', Solver.CBC_MIXED_INTEGER_PROGRAMMING) a = solver.IntVar(-limit, limit, 'a') b = solver.IntVar(-limit, limit, 'b') c = solver.IntVar(-limit, limit, 'c') d = solver.IntVar(-limit, limit, 'd') f = solver.NumVar(0, 1, 'f') solver.Add(f == (a * x1 + b * x2 + c * x3 + d * x4)) solver.Add(a * x1 + b >= 1) # don't except trivial solutions and remove some redundancy solver.Minimize(f) status = solver.Solve() if status == Solver.OPTIMAL: if abs(solver.Objective().Value()) <= threshold: res_a, res_b, res_c, res_d = int(a.solution_value()), int(b.solution_value()),\ int(c.solution_value()), int(d.solution_value()) ret = MobiusTransform( np.array([[res_a, res_b], [res_c, res_d]], dtype=object)) ret.normalize() return ret else: return None
def _solve_optimization(self, solver: pywraplp.Solver) -> None: # The MIP solver is usually fast (milliseconds). If we hit a weird problem, # accept a suboptimal solution after 10 seconds. solver.SetTimeLimit(10000) status = solver.Solve() if status == pywraplp.Solver.INFEASIBLE: raise Exception("Infeasible problem") elif status == pywraplp.Solver.NOT_SOLVED: raise Exception("Problem unsolved")
def _attempt_to_improve_var_bounds_one_hs(solver: pywraplp.Solver, coefficient_part: str, is_leq: bool, right_hand_side: str): coefficient_part = coefficient_part.strip() if re.match(_REGEXP_SINGLE_VAR_NAME_ALL, coefficient_part): if is_leq: solver.LookupVariable(coefficient_part).SetUb( float(right_hand_side)) else: solver.LookupVariable(coefficient_part).SetLb( float(right_hand_side))
def main(): # Create the linear solver with the GLOP backend. solver = Solver('simple_lp_program', Solver.GLOP_LINEAR_PROGRAMMING) # Create the variables x and y. x = solver.NumVar(0, 1, 'x') y = solver.NumVar(0, 2, 'y') print('Number of variables =', solver.NumVariables()) # Create a linear constraint, 0 <= x + y <= 2. ct = solver.Constraint(0, 2, 'ct') ct.SetCoefficient(x, 1) ct.SetCoefficient(y, 1) print('Number of constraints =', solver.NumConstraints()) # Create the objective function, 3 * x + y. objective = solver.Objective() objective.SetCoefficient(x, 3) objective.SetCoefficient(y, 1) objective.SetMaximization() solver.Solve() print('Solution:') print('Objective value =', objective.Value()) print('x =', x.solution_value()) print('y =', y.solution_value())
def _solve_optimization(self, solver: pywraplp.Solver) -> bool: # The MIP solver is usually fast (milliseconds). If we hit a weird problem, # accept a suboptimal solution after 10 seconds. solver.SetTimeLimit(10000) status = solver.Solve() if status == pywraplp.Solver.INFEASIBLE: logger.warning("Optimization problem is infeasible") return False elif status == pywraplp.Solver.NOT_SOLVED: logger.warning("Optimization problem could not be solved in time") return False return True
def solve_problem() -> None: solver: Solver = Solver("simple_mip_program", Solver.CBC_MIXED_INTEGER_PROGRAMMING) x: Any = solver.IntVar(0, 100, "x") y: Any = solver.IntVar(0, 100, "y") solver.Add(1 * x + 2 * y <= 40) solver.Add(4 * x + 4 * y <= 80) solver.Add(3 * x + 1 * y <= 50) solver.Maximize(x * 5.0 + y * 4.0) status: Any = solver.Solve() if status == Solver.OPTIMAL or status == Solver.FEASIBLE: print("Solution: OK") print("Objective value =", solver.Objective().Value()) if x.solution_value() > 0.5: print("x =", x.solution_value()) print("y =", y.solution_value()) print("Time = ", solver.WallTime(), " milliseconds") else: print("The problem does not have an optimal solution.")
def _parse_objective_function(solver: pywraplp.Solver, core_line: str, line_nr: int, var_names: set): spl_colon = core_line.split(":", maxsplit=1) objective = solver.Objective() # Set maximization / minimization if specified if len(spl_colon) == 1: raise ValueError( "Objective function on line %d must start with \"max:\" or \"min:\"." % line_nr) else: if spl_colon[0] != "max" and spl_colon[0] != "min": raise ValueError( "Objective function on line %d must start with \"max:\" or \"min:\"." % line_nr) elif spl_colon[0] == "max": objective.SetMaximization() else: objective.SetMinimization() # Set the remainder coefficient_part = spl_colon[1].strip() # Finally set the coefficients of the objective constant = _set_coefficients(solver, objective, coefficient_part, line_nr, var_names) objective.SetOffset(constant)
def _declare_constraint( solver: pywraplp.Solver, group: "Group", variables: typing.Tuple[typing.Tuple[pywraplp.Variable]], ) -> None: higher_groups = group.get_simultaneous_groups_of_higher_cycle() if higher_groups: # fixed duration of group summed_active_transitions_of_group = Group._sum_active_transitions_of_group( group, higher_groups) # fixed duration of higher groups summed_transitions_of_higher_groups = sum( Group._calculate_duration_of_n_phases(higher_group, higher_group.attack) for higher_group in higher_groups) summed_transitions_of_higher_groups += Group._calculate_duration_of_n_phases( higher_groups[-1], higher_groups[-1].release) difference_of_duration = (summed_active_transitions_of_group - summed_transitions_of_higher_groups) constraint = sum( Group._calculate_duration_of_n_phases(higher_group, var) for var, higher_group in zip(variables[1], higher_groups)) constraint = constraint - (Group._calculate_duration_of_n_phases( group, variables[0][0])) solver.Add(constraint == difference_of_duration)
def _declare_variable(self, solver: pywraplp.Solver, nth_cycle: int, nth_index: int) -> None: return solver.NumVar( MIN_N_PHASES_FOR_SUSTAIN, MIN_N_PHASES_FOR_SUSTAIN * MAX_N_MIN_PHASES_FOR_SUSTAIN, self._get_variable_name(nth_cycle, nth_index), )
def solve_problem() -> None: solver: Solver = Solver("simple_mip_program", Solver.CBC_MIXED_INTEGER_PROGRAMMING) backet: Dict = {j: solver.BoolVar(f"x{j}") for j in range(5)} solver.Add( solver.Sum(list(VOLUMES[j] * backet[j] for j in range(len(VOLUMES)))) <= CAPASITY) solver.Maximize( solver.Sum(list(VALUES[j] * backet[j] for j in range(len(VOLUMES))))) status: Any = solver.Solve() if status == Solver.OPTIMAL or status == Solver.FEASIBLE: print("Solution: OK") print("Objective value =", solver.Objective().Value()) print("culculate Time = ", solver.WallTime(), " milliseconds") print("select item") for j in range(len(VOLUMES)): print(j, backet[j].solution_value()) print("total value") print( sum(VALUES[j] * backet[j].solution_value() for j in range(len(VOLUMES)))) else: print("The problem does not have an optimal solution.")
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 __init__(self, prob_input: Input, solver_id: str, solver_output: bool): self.prob_input = prob_input self.solver_id = solver_id self.solver = Solver.CreateSolver(solver_id) self.production_vars = None self.configuration_vars = None self.stock_vars = None self.transition_vars = None self.pred_configuration_vars = None self.succ_configuration_vars = None if solver_output: self.solver.EnableOutput() else: self.solver.SuppressOutput()
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 cap_mip(customers, facilities, max_time=60): n_fac = len(facilities) n_cust = len(customers) solver = Solver.CreateSolver("FacilityLocation", "SCIP") x = [] y = [] for f in range(n_fac): y.append([solver.BoolVar(f"y_{f}_{c}") for c in range(n_cust)]) x.append(solver.BoolVar(f"x_{f}")) caps = [f.capacity for f in facilities] setup = [f.setup_cost for f in facilities] dist = distance_matrix(customers, facilities).astype(int) demands = [c.demand for c in customers] for f in range(n_fac): for c in range(n_cust): solver.Add(y[f][c] <= x[f]) for c in range(n_cust): solver.Add(sum([y[f][c] for f in range(n_fac)]) == 1) for f in range(n_fac): solver.Add(sum([y[f][c] * demands[c] for c in range(n_cust)]) <= caps[f]) obj = 0 for f in range(n_fac): obj += setup[f] * x[f] obj += sum([dist[f][c] * y[f][c] for c in range(n_cust)]) solver.Minimize(obj) STATUS = { Solver.FEASIBLE: "FEASIBLE", Solver.UNBOUNDED: "UNBOUNDED", Solver.BASIC: "BASIC", Solver.INFEASIBLE: "INFEASIBLE", Solver.NOT_SOLVED: "NOT_SOLVED", Solver.OPTIMAL: "OPTIMAL", } solver.SetTimeLimit(max_time * 1000) status = solver.Solve() STATUS[status] a = [] for f in range(n_fac): a.append([y[f][c].solution_value() for c in range(n_cust)]) sol = np.array(a).argmax(axis=0) return sol, STATUS[status]
def _add_production_variables(num_types: int, num_time_periods: int, solver: Solver) -> Dict[V, Variable]: """Create production variables. A production variable $x^t_p$ is a binary variable which is one if and only if an item of type $t$ is produced 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 production variable to its solver variable """ production_vars = dict() for (item_type, time_period) in product(range(num_types), range(num_time_periods)): production_vars[V(type=item_type, period=time_period)] = solver.BoolVar( name=f'x_{item_type}_{time_period}') module_logger.info( f'Created {len(production_vars)} production variables.') return production_vars
def _add_configuration_variables(num_types: int, num_time_periods: int, solver: Solver) \ -> Dict[V, Variable]: """Create configuration variables. A configuration variable $y^t_p$ is a binary variable which is one if and only if the machine is configured for type $t$ 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 configuration tuple to its solver variable """ configuration_vars = dict() for (item_type, time_period) in product(range(num_types), range(num_time_periods)): configuration_vars[V(type=item_type, period=time_period)] = solver.BoolVar( name=f'y_{item_type}_{time_period}') module_logger.info( f'Created {len(configuration_vars)} configuration variables.') return configuration_vars
def solve_problem() -> None: solver: Solver = Solver("simple_mip_program", Solver.CBC_MIXED_INTEGER_PROGRAMMING) amount: Dict = {} for i in range(3): for j in range(4): amount[i, j] = solver.IntVar(0, 50, f"val{i}_{j}") amount_w1, amount_w2, amount_w3 = get_quantity_carried_out(amount) solver.Add(amount_w1 <= 35) solver.Add(amount_w2 <= 41) solver.Add(amount_w3 <= 42) amount_f1, amount_f2, amount_f3, amount_f4 = get_quantity_carried_in( amount) solver.Add(amount_f1 >= 28) solver.Add(amount_f2 >= 29) solver.Add(amount_f3 >= 31) solver.Add(amount_f4 >= 25) solver.Minimize( solver.Sum( [amount[i, j] * COST[i][j] for i in range(3) for j in range(4)])) status: Any = solver.Solve() if status == Solver.OPTIMAL or status == Solver.FEASIBLE: print("Solution: OK") print("Objective value =", solver.Objective().Value()) print("=============================================") for i in range(3): for j in range(4): if amount[i, j].SolutionValue() > 0: print(i, j, amount[i, j].SolutionValue()) print("Time = ", solver.WallTime(), " milliseconds") else: print("The problem does not have an optimal solution.")
def _add_transition_variables(num_types: int, num_time_periods: int, solver: Solver) \ -> Dict[W, Variable]: """Create transition variables. A transition variable $u^ij_p$ is a binary variable which is one if and only if the machine's configuration changes from type $i$ to type $j$ 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 transition variable to its solver variable """ transition_vars = dict() for type_i, type_j, time_period in product(range(num_types), range(num_types), range(1, num_time_periods)): transition_vars[W(from_type=type_i, to_type=type_j, from_period=time_period - 1, to_period=time_period)] = solver.BoolVar( name=f'u_{type_i}_{type_j}_{time_period}') module_logger.info( f'Created {len(transition_vars)} transition variables.') return transition_vars
def cap_mip2(customers, facilities, max_time=60, min_fac=None, max_fac=None, k_neigh=None): n_fac = len(facilities) n_cust = len(customers) solver = Solver.CreateSolver("FacilityLocation", "SCIP") if min_fac is None: min_fac = min_facilities(customers, facilities) print(f'Minimum Facilities: {min_fac}') est_fac = est_facilities(customers, facilities) est_fac = max(5, min_fac) print('Estimated Facilities:', est_fac) if k_neigh is None: k_neigh = n_fac // est_fac * 2 k_neigh = min(k_neigh, n_fac // 2) print(f'Only using {k_neigh} nearest facilities') # Estimate Customers per Facilitiy cpf = n_cust // min_fac cpf = np.clip(cpf, 2, n_cust // 2) # Define Variables x = [] y = [] for f in range(n_fac): y.append([solver.BoolVar(f"y_{f}_{c}") for c in range(n_cust)]) x.append(solver.BoolVar(f"x_{f}")) caps = np.array([f.capacity for f in facilities]) setup = np.array([f.setup_cost * 100 for f in facilities]) dist = distance_matrix(customers, facilities) * 100 dist = dist.astype(int) demands = np.array([c.demand for c in customers]) # Problem Analysis free_setup = np.where(caps == 0)[0] is_fixed_setup = True if np.std(caps[caps > 0]) == 0 else False # Add Constraints print('\t Adding Constaints') # If facility is closed then it is not connected to any customer for f in range(n_fac): for c in range(n_cust): solver.Add(y[f][c] <= x[f]) # Each customer is connected to only one facility for c in range(n_cust): solver.Add(sum([y[f][c] for f in range(n_fac)]) == 1) # The demand is not more than the capacity of the facility for f in range(n_fac): solver.Add( sum([y[f][c] * demands[c] for c in range(n_cust)]) <= caps[f] * x[f]) solver.Add( sum([y[f][c] * demands[c] for c in range(n_cust)]) <= caps[f]) # Customers per facility for f in range(n_fac): solver.Add(sum([y[f][c] for c in range(n_cust)]) <= n_cust * x[f]) # The free facilities must be open for f in free_setup: solver.Add(x[f] == 1) # Customer can ONLY connect to nearby facilities for c in range(n_cust): idx = np.argsort(dist[:, c]) for f in idx[k_neigh:]: solver.Add(y[f][c] == 0) print('\t Adding Covercut Constaints') # Cover cut for customers which can be connected to a facility # round1 = [] # round2 = [] # for f in range(n_fac): # argsort = np.argsort(dist[f, :]) # idx = argsort[:cpf] # if sum(demands[idx]) > caps[f]: # round1.append(f) # solver.Add(sum([y[f][c] for c in idx]) <= (cpf - 1)) # if cpf > 4: # k = int(cpf * .9) # idx = argsort[:k] # if sum(demands[idx]) > caps[f]: # round2.append(f) # solver.Add(sum([y[f][c] for c in idx]) <= (k - 1)) # print(round1) # print(round2, flush=True) # Maximum Facility open solver.Add(sum(x) >= min_fac) if max_fac is not None: solver.Add(sum(x) <= max_fac) # solver.Add(sum(x)>=2) # Define objective obj = 0 for f in range(n_fac): # Setup cost if not is_fixed_setup: obj += setup[f] * x[f] # Service cost obj += sum([dist[f][c] * y[f][c] for c in range(n_cust)]) solver.Minimize(obj) STATUS = { Solver.FEASIBLE: "FEASIBLE", Solver.UNBOUNDED: "UNBOUNDED", Solver.BASIC: "BASIC", Solver.INFEASIBLE: "INFEASIBLE", Solver.NOT_SOLVED: "NOT_SOLVED", Solver.OPTIMAL: "OPTIMAL", } solver.SetTimeLimit(max_time * 1000) # Solve print('\t Starting the Solver') status = solver.Solve() STATUS[status] # Retreive values a = [] for f in range(n_fac): a.append([y[f][c].solution_value() for c in range(n_cust)]) # Convert solution matrix to facility index sol = np.array(a).argmax(axis=0) return sol, STATUS[status]
#!/bin/python from ortools.linear_solver import pywraplp from ortools.linear_solver.pywraplp import Solver # A person needs three nutrients. Let's assume they are vitamins A, B and C. Nutrition = [2000, 300, 430] # They can be supplied by four foods. The first number if the calories supplied # by a food type. The remaining three numbers are the nutrients. Foods = [['Trout', 600, 203, 92, 100], ['CB Sandwich', 350, 90, 84, 230], ['Burrito', 250, 270, 80, 512], ['Hamburger', 500, 500, 90, 210]] # A person needs a certain minimum calories. MinCalories = 2500 solver = Solver.CreateSolver('diet', 'CBC') # The decision variables. How much quantity of each food typu should a person # consume? consumption = [None] * len(Foods) for i in range(0, len(Foods)): consumption[i] = solver.IntVar(1, solver.infinity(), Foods[i][0]) # The objective function is to minimize the number of calories. objf = solver.Objective() for i in range(0, len(Foods)): objf.SetCoefficient(consumption[i], Foods[i][1]) objf.SetMinimization() # Add constraints. Unlike the previous examples where we had our constraints # given to us as inequalities, here we will have to build them from the given # data.
def _compute_compact_linearisation_sets(self, *, z_weight: float, f_weight: float) \ -> Tuple[Dict[int, List[V]], List[Tuple[V, V]]]: """Computes sets F and B_k used in Liberti's compact formulation.""" _solver = Solver.CreateSolver(self.solver_id) f_vars = dict() z_vars = dict() N = bidict() for time_period in range(self.prob_input.num_time_periods): for item_type in range(self.prob_input.num_types): N[V(type=item_type, period=time_period)] = 1 + len(N) M = lambda i: (j for j in N.values() if i <= j) K = list(range(self.prob_input.num_time_periods)) # create f_ij \in [0,1] for all 1 <= i <= j <= n for i in N.values(): for j in M(i): f_vars[(i, j)] = _solver.NumVar(lb=0., ub=1., name=f'f_{str(i)}{str(j)}') # create z_ik \in {0,1} for all k \in K, 1 <= i <= n for k in K: for i in N.values(): z_vars[(i, k)] = _solver.BoolVar(name=f'z_{str(i)}{str(k)}') # add constraints: f_ij = 1 \forall (i,j) \in E for var in self.transition_vars.keys(): i = N[V(type=var.from_type, period=var.from_period)] j = N[V(type=var.to_type, period=var.to_period)] _solver.Add(f_vars[(i, j)] == 1, name='cons_10') # add constraints: f_ij >= z_jk \forall k \in K, i \in A_k, j \in N, i <= j for k in K: A_k = [ N[V(type=item_type, period=k)] for item_type in range(self.prob_input.num_types) ] for i in A_k: for j in M(i): lhs = f_vars[(i, j)] rhs = z_vars[(j, k)] _solver.Add(lhs >= rhs, name='cons_11') # add constraints: f_ji >= z_jk \forall k \in K, i \in A_k, j \ín N, j < i for k in K: A_k = [ N[V(type=item_type, period=k)] for item_type in range(self.prob_input.num_types) ] for i in A_k: for j in (j for j in N.values() if j < i): lhs = f_vars[(j, i)] rhs = z_vars[(j, k)] _solver.Add(lhs >= rhs, name='cons_12') # add constraints: \sum_{k: i \in A_k} z_jk >= f_ij \forall 1 <= i <= j <= n for i in N.values(): for j in M(i): k = N.inverse[i].period lhs = z_vars[(j, k)] rhs = f_vars[(i, j)] _solver.Add(lhs >= rhs, name='cons_13') # add constraints: \sum_{k: j \in A_k} z_ik >= f_ij \forall 1 <= i <= j <= n for i in N.values(): for j in M(i): k = N.inverse[j].period lhs = z_vars[(i, k)] rhs = f_vars[(i, j)] _solver.Add(lhs >= rhs, name='cons_14') # add objective z_obj = _solver.Sum(z_weight * var for var in z_vars.values()) f_obj = _solver.Sum(f_weight * var for var in f_vars.values()) _solver.Minimize(z_obj + f_obj) # solve status = _solver.Solve() if status == Solver.OPTIMAL: B = defaultdict(list) for i, k in (key for key, var in z_vars.items() if var.solution_value() > 0.99): B[k].append(N.inverse[i]) F = [(N.inverse[i], N.inverse[j]) for (i, j), var in f_vars.items() if var.solution_value() > 0.99] return B, F else: raise RuntimeError('No optimal solution found.')
# certain number of items. # # Suppose that we are given items of a certain weight. They should be # transported through wagons of a certain capacity. What is the least number # of wagons that we should hire to carry all goods? itemWts = [48, 30, 19, 36, 36, 27, 42, 42, 36, 24, 30] maxwt = 100 # We ensure that every item can be fitted in a wagon. That is, none of them # has a weight more than the wagon's capacity. from ortools.linear_solver import pywraplp from ortools.linear_solver.pywraplp import Solver solver = Solver.CreateSolver('bin-packing', 'CBC') # The decision variables are of the form x_{ij}. They take a value 1 if # item i is put in wagon j, 0 otherwise. nItems = len(itemWts) nWagons = nItems x = {} for i in range(0, nItems): for j in range(0, nWagons): vname = f'x[{i}, {j}]' x[(i, j)] = solver.IntVar(0, 1, vname) # Recall that we need at most as many wagons as we have items. w[i] is 1 # is wagon i is used. w = [None] * nWagons
# The data. nResources = 7 nItems = 12 resourceAvailability = [18209, 7692, 1333, 924, 26638, 61188, 13360] itemValue = [96, 76, 56, 11, 86, 10, 66, 86, 83, 12, 9, 81] resourceUse = [ [19, 1, 10, 1, 1, 14, 152, 11, 1, 1, 1, 1], [ 0, 4, 53, 0, 0, 80, 0, 4, 5, 0, 0, 0], [ 4, 660, 3, 0, 30, 0, 3, 0, 4, 90, 0, 0], [ 7, 0, 18, 6, 770, 330, 7, 0, 0, 6, 0, 0], [ 0, 20, 0, 4, 52, 3, 0, 0, 0, 5, 4, 0], [ 0, 0, 40, 70, 4, 63, 0, 0, 60, 0, 4, 0], [ 0, 32, 0, 0, 0, 5, 0, 3, 0, 660, 0, 9]] solver = Solver.CreateSolver('multi-knapsack', 'CBC') # How many numbers of each items should be packed? Each one of these is a # decision variable. take = [None] * nItems for i in range(0, nItems): take[i] = solver.IntVar(0, solver.infinity(), f'take[{i}]') # Build the objective function. objf = solver.Objective() for i in range(0, nItems): objf.SetCoefficient(take[i], itemValue[i]) objf.SetMaximization() for i in range(0, nResources):
[5, 7, 9, 2, 1], [18, 4, -9, 10, 12], [4, 7, 3, 8, 5], [5, 13, 16, 3, -7], ] data['bounds'] = [250, 285, 211, 315] data['obj_coeffs'] = [7, 8, 2, 9, 6] data['num_vars'] = 5 data['num_constraints'] = 4 return data data = create_data_model() solver = Solver.CreateSolver('mip', 'CBC') # Quite like in the case of the 'Diet problem' the decision variables are # created as elements of a list. x = [None] * data['num_vars'] for i in range(0, len(x)): x[i] = solver.IntVar(0, solver.infinity(), f'x[{i}]') print(f'Number of variables = {solver.NumVariables()}') # Build the constraints. for i in range(0, data['num_constraints']): cn = solver.Constraint(0, data['bounds'][i], f'cn[{i}]') for j in range(0, data['num_vars']): cn.SetCoefficient(x[j], data['constraint_coeffs'][i][j]) print(f'Number of constraints = {solver.NumConstraints()}')
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)
def init_model(): global _MODEL _MODEL = Solver("No name", Solver.CBC_MIXED_INTEGER_PROGRAMMING)
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