def __init__(self): self.prob = LpProblem('pydfs_lineup_optimizer', LpMaximize)
def optimise_selection( schedule_input: pd.DataFrame, selection_limit: int, black_points_limit: int, rounds: List[str], loser: Optional[str] = None, ) -> Dict[str, Any]: """ Optimise player selection. :param schedule_input: Players and their schedule. :param selection_limit: Number of players to choose. :param black_points_limit: Maximum number of black points. :param rounds: Rounds to play :param loser: Selected loser :return: Optimal selection """ LOGGER.info("Optimising selection") schedule = schedule_input.copy() black_extra = 0 selection_limit_extra = 0 extra_loss = 0 if loser: loser_record = schedule[schedule["player"].str.lower() == loser.lower()].iloc[0] if loser_record.empty: LOGGER.warning("Unable to find player %s in draw", loser) loser = None else: loser = loser_record.player # extra_loss = TennisPoolEmulator.rounds_to_score( # rounds=loser_record.rounds, # black=loser_record.black, # loser=True # ) extra_loss = TennisPoolEmulator.probabilities_to_score( round_probs=loser_record[rounds], black=loser_record.black, loser=True) schedule.loc[schedule.player == loser, "potency"] = extra_loss black_extra = loser_record.black selection_limit_extra = 1 LOGGER.info( "Allowing %d extra black points, because of your selected loser", black_extra, ) LOGGER.info( "Adding the loser to your selection, and simultaneously increasing the limit of your" "selection, such that your loser is taken into account.") black_points_limit += black_extra players = schedule["player"].tolist() potency = schedule["potency"].tolist() black_points = schedule["black"].tolist() param_player = range(len(schedule)) # Declare problem instance, maximization problem probability = LpProblem("PlayerSelection", LpMaximize) # Declare decision variable x, which is 1 if a # player is part of the selection and 0 else param_x = LpVariable.matrix("x", list(param_player), 0, 1, LpInteger) # Objective function -> Maximise potency probability += sum(potency[p] * param_x[p] for p in param_player) # Constraint definition probability += sum(param_x[p] for p in param_player) == (selection_limit + selection_limit_extra) probability += (sum(black_points[p] * param_x[p] for p in param_player) <= black_points_limit) if loser: probability += param_x[players.index(loser)] == 1 # Start solving the problem instance probability.solve() # Extract solution player_selection = [ players[p] for p in param_player if param_x[p].varValue ] LOGGER.info("Optimiser finished") if loser: LOGGER.warning( "Do note you're going to lose %d points because of your loser.", -extra_loss) return { "schedule": schedule[schedule["player"].isin( player_selection)].copy().reset_index(), "loser": extra_loss, }
def __init__(self, **kwargs): self.name = kwargs.get('name', 'DFSModel') self.salary_cap = kwargs.get('salary_cap', 50000) self.model = LpProblem(self.name, LpMaximize)
def test_result_size(self): function_result = solve_linear_problem(LpProblem()) self.assertEqual(len(function_result), 3)
# This collection of models is licensed under The Creative Commons # # CC BY-SA 3.0 License, a copy of which is available here: # # # # http://creativecommons.org/licenses/by-sa/3.0/ # # # # Copyright 2014, Ted Ralphs # # All Rights Reserved # # [email protected] # # # ####################################################################### from pulp import LpProblem, LpVariable, lpSum, LpMaximize, value, LpStatus from bonds_data import bonds, max_rating, max_maturity, max_cash prob = LpProblem("Bond Selection Model", LpMaximize) buy = LpVariable.dicts('bonds', bonds.keys(), 0, None) prob += lpSum(bonds[b]['yield'] * buy[b] for b in bonds) prob += lpSum(buy[b] for b in bonds) <= max_cash, "cash" prob += lpSum(bonds[b]['rating'] * buy[b] for b in bonds) <= max_cash * max_rating, "ratings" prob += lpSum(bonds[b]['maturity'] * buy[b] for b in bonds) <= max_cash * max_maturity, "maturities" status = prob.solve()
# Problem Data feed: List = [0, 1, 2, 3, 4, 5] costs: Dict = {0: 0.74, 1: 0.70, 2: 0.83, 3: 0.81, 4: 0.73, 5: 0.75} minimum: Dict = {0: 200, 1: 180, 2: 150} # Carbohydrate # Protein # Vitamin inf_nutri: List = [ [50, 60, 30, 0, 20, 45], [27, 0, 40.5, 20, 30, 50], [50, 80, 60, 30, 20, 40], ] # Model and Decision Variables model = LpProblem("Diet_problem", LpMinimize) var = LpVariable.dict("R", feed, lowBound=0, cat="Integer") # Goal function model += lpSum(var[x] * costs[x] for x in var.keys()) # Constrains list_rest: List = [] for i in minimum.keys(): for j in feed: list_rest.append(var[j] * inf_nutri[i][j]) model += lpSum(list_rest) >= minimum[i] list_rest = [] print(model)
def generate_mincost_relaxations(candidate, negative_cycle, feasibility_type): all_cycles = candidate.continuously_resolved_cycles.copy() all_cycles.add(negative_cycle) # print("Solving against " + str(len(all_cycles)) + " cycles") # Solve using PuLP, TODO, incorporate interface to other solvers, # especially for nonlinear objective functions prob = LpProblem("MinCostConflictResolution", LpMinimize) # Solve using PyOpt/Snopt, # especially for nonlinear constraints/objective functions # construct variables and constraints lp_variables = {} lp_objective = [] # status indicating the feasibility of the relaxation problem # 1 is feasible # 0 is infeasible # Note that this variable is shared by PuLP for its result status = 1 # print("Cycles: " + str(len(all_cycles))) for cycle in all_cycles: # cycle.pretty_print() lp_ncycle_constraint = [] for constraint, bound in cycle.constraints.keys(): # The constraint is a pair (temporal_constraint,0/1) # where 0 or 1 represent if it is the lower or upper bound # first we define the variables # which only come from relaxable bounds of constraints # in other words, if no constraint in a negative cycle is # relaxable, the LP is infeasible # and we can stop here # TODO: add handler for uncontrollable duration variable = None if (constraint, bound) in lp_variables: variable = lp_variables[(constraint, bound)] coefficient = cycle.constraints[(constraint, bound)] if variable is None: if bound == 0: # lower bound, the domain is less than the original LB # if the constraint is not relaxable, fix its domain if constraint.relaxable_lb: # print("LB cost " + str(constraint.relax_cost_lb) + "/" + constraint.name) if constraint.controllable or feasibility_type == FeasibilityType.CONSISTENCY: variable = LpVariable(constraint.id + "-LB", None, constraint.lower_bound) # print("New LB VAR: " + str(variable) + " [" + str(None) + "," + str(constraint.lower_bound) + "]") # add the variable to the objective function lp_variables[(constraint, bound)] = variable lp_objective.append( (constraint.lower_bound - variable) * constraint.relax_cost_lb) else: variable = LpVariable( constraint.id + "-LB", constraint.lower_bound, constraint.upper_bound - 0.001) # print("New LB-UC VAR: " + str(variable) + " [" + str(None) + "," + str(constraint.lower_bound) + "]") # add the variable to the objective function lp_variables[(constraint, bound)] = variable lp_objective.append( (variable - constraint.lower_bound) * constraint.relax_cost_lb) # Add an additional constraint to make sure that # the lower bound is smaller than the upper bound if (constraint, 1) in lp_variables: ub_variable = lp_variables[(constraint, 1)] uncertain_duration_constraint = [] uncertain_duration_constraint.append( (ub_variable - variable)) prob += sum( uncertain_duration_constraint) >= 0.001 # print("Adding domain constraint for " + constraint.name) else: variable = constraint.lower_bound elif bound == 1: # upper bound, the domain is larger than the original UB # if the constraint is not relaxable, fix its domain if constraint.relaxable_ub: # print("UB cost " + str(constraint.relax_cost_ub) + "/" + constraint.name) if constraint.controllable or feasibility_type == FeasibilityType.CONSISTENCY: variable = LpVariable(constraint.id + "-UB", constraint.upper_bound, None) # print("New UB VAR: " + str(variable) + " [" + str(constraint.upper_bound) + "," + str(None) + "]") # add the variable to the objective function lp_variables[(constraint, bound)] = variable lp_objective.append( (variable - constraint.upper_bound) * constraint.relax_cost_ub) else: variable = LpVariable( constraint.id + "-UB", constraint.lower_bound + 0.001, constraint.upper_bound) # print("New UB-UC VAR: " + str(variable) + " [" + str(constraint.upper_bound) + "," + str(None) + "]") lp_variables[(constraint, bound)] = variable lp_objective.append( (constraint.upper_bound - variable) * constraint.relax_cost_ub) # Add an additional constraint to make sure that # the lower bound is smaller than the upper bound if (constraint, 0) in lp_variables: lb_variable = lp_variables[(constraint, 0)] uncertain_duration_constraint = [] uncertain_duration_constraint.append( (variable - lb_variable)) prob += sum( uncertain_duration_constraint) >= 0.001 # print("Adding domain constraint for " + constraint.name) else: variable = constraint.upper_bound assert variable is not None # print("Adding variable " + str(coefficient) + "*"+str(variable)) lp_ncycle_constraint.append(variable * coefficient) # add the constraint to the problem # print(str(lp_ncycle_constraint)) if sum(lp_ncycle_constraint) >= 0: # print(str(sum(lp_ncycle_constraint)) + " >= 0") # Over relax a bit to account for precision issues prob += sum(lp_ncycle_constraint) >= 0.0 else: status = 0 # this is not resolvable # no need to proceed if status > 0: # Set the objective function prob += sum(lp_objective) # for c in prob.constraints: # print("CON: ", prob.constraints[c]) # print("OBJ: ", prob.objective) # Solve the problem try: import gurobipy status = prob.solve(pulp.GUROBI(mip=False, msg=False)) except ImportError: # pass # Gurobi doesn't exist, use default Pulp solver. status = prob.solve() # exit(0); # print("Computing relaxation") # if no solution was found, do nothing if status > 0: # A solution has been found # extract the result and store them into a set of relaxation # the outcome is a set of relaxations relaxations = [] # print("Found relaxation") for constraint, bound in lp_variables.keys(): variable = lp_variables[(constraint, bound)] relaxed_bound = value(variable) if bound == 0: # check if this constraint bound is relaxed if relaxed_bound != constraint.lower_bound: # yes! create a new relaxation for it relaxation = TemporalRelaxation(constraint) relaxation.relaxed_lb = relaxed_bound # relaxation.pretty_print() relaxations.append(relaxation) assert relaxation.relaxed_lb <= constraint.upper_bound elif bound == 1: # same for upper bound if relaxed_bound != constraint.upper_bound: # yes! create a new relaxation for it relaxation = TemporalRelaxation(constraint) relaxation.relaxed_ub = relaxed_bound # relaxation.pretty_print() relaxations.append(relaxation) assert relaxation.relaxed_ub >= constraint.lower_bound # print("") if len(relaxations) > 0: return relaxations, 0 return None, 0
def ilp_cgdp( cg: ComputationGraph, agentsdef: Iterable[AgentDef], footprint: Callable[[str], float], capacity: Callable[[str], float], route: Callable[[str, str], float], msg_load: Callable[[str, str], float], hosting_cost: Callable[[str, str], float], timeout=600, # Max 10 min ): start_t = time.time() agt_names = [a.name for a in agentsdef] pb = LpProblem("oilp_cgdp", LpMinimize) # One binary variable xij for each (variable, agent) couple xs = LpVariable.dict("x", (cg.node_names(), agt_names), cat=LpBinary) # TODO: Do not create var for computation that are already assigned to an agent with hosting = 0 ? # Force computation with hosting cost of 0 to be hosted on that agent. # This makes the work much easier for glpk ! x_fixed_to_0 = [] x_fixed_to_1 = [] for agent in agentsdef: for comp in cg.node_names(): assigned_agent = None if agent.hosting_cost(comp) == 0: pb += xs[(comp, agent.name)] == 1 x_fixed_to_1.append((comp, agent.name)) assigned_agent = agent.name for other_agent in agentsdef: if other_agent.name == assigned_agent: continue pb += xs[(comp, other_agent.name)] == 0 x_fixed_to_0.append((comp, other_agent.name)) logger.debug( f"Setting binary varaibles to fixed computation {comp}") # One binary variable for computations c1 and c2, and agent a1 and a2 betas = {} count = 0 for a1, a2 in combinations(agt_names, 2): # Only create variables for couple c1, c2 if there is an edge in the # graph between these two computations. for l in cg.links: # As we support hypergraph, we may have more than 2 ends to a link for c1, c2 in combinations(l.nodes, 2): if (c1, a1, c2, a2) in betas: continue count += 2 b = LpVariable("b_{}_{}_{}_{}".format(c1, a1, c2, a2), cat=LpBinary) betas[(c1, a1, c2, a2)] = b # Linearization constraints : # a_ijmn <= x_im # a_ijmn <= x_jn if (c1, a1) in x_fixed_to_0 or (c2, a2) in x_fixed_to_0: pb += b == 0 elif (c1, a1) in x_fixed_to_1: pb += b == xs[(c2, a2)] elif (c2, a2) in x_fixed_to_1: pb += b == xs[(c1, a1)] else: pb += b <= xs[(c1, a1)] pb += b <= xs[(c2, a2)] pb += b >= xs[(c2, a2)] + xs[(c1, a1)] - 1 b = LpVariable("b_{}_{}_{}_{}".format(c1, a2, c2, a1), cat=LpBinary) if (c1, a2) in x_fixed_to_0 or (c2, a1) in x_fixed_to_0: pb += b == 0 elif (c1, a2) in x_fixed_to_1: pb += b == xs[(c2, a1)] elif (c2, a1) in x_fixed_to_1: pb += b == xs[(c1, a2)] else: betas[(c1, a2, c2, a1)] = b pb += b <= xs[(c2, a1)] pb += b <= xs[(c1, a2)] pb += b >= xs[(c1, a2)] + xs[(c2, a1)] - 1 # Set objective: communication + hosting_cost pb += ( _objective(xs, betas, route, msg_load, hosting_cost), "Communication costs and prefs", ) # Adding constraints: # Constraints: Memory capacity for all agents. for a in agt_names: pb += ( lpSum([footprint(i) * xs[i, a] for i in cg.node_names()]) <= capacity(a), "Agent {} capacity".format(a), ) # Constraints: all computations must be hosted. for c in cg.node_names(): pb += ( lpSum([xs[c, a] for a in agt_names]) == 1, "Computation {} hosted".format(c), ) # the timeout for the solver must be minored by the time spent to build the pb: remaining_time = round(timeout - (time.time() - start_t)) - 2 # solve using GLPK try: status = pb.solve(solver=GLPK_CMD( keepFiles=0, msg=False, options=["--pcost", "--tmlim", str(remaining_time)])) except PulpSolverError as pse: raise ImpossibleDistributionException(f"Pulp error {pse}", pse) if status == LpStatusUndefined: # Generally means we have reach the timeout. raise TimeoutError(f"Could not find solution in {timeout}") if status != LpStatusOptimal: raise ImpossibleDistributionException( f"No possible optimal distribution {status}") logger.debug("GLPK cost : %s", pulp.value(pb.objective)) mapping = {} for k in agt_names: agt_computations = [ i for i, ka in xs if ka == k and pulp.value(xs[(i, ka)]) == 1 ] # print(k, ' -> ', agt_computations) mapping[k] = agt_computations return mapping
# constraints_matrix = np.transpose([sov_mv, sov_mv_l, corp_mv, illiquid_mv, illiquid_mv_l, bbb_mv, below_bbb_mv]) # sov_mv, sov_mv_l, corp_mv, illiquid_mv, illiquid_mv_l, bbb_mv, below_bbb_mv # #print(constraints_matrix) # # define the A_ub matrix # # rows: 47 20 20 7 # opt_matrix = np.concatenate((main_matrix, funding_gap_matrix, constraints_matrix), axis=1) # 200 x 47 # #opt_matrix = np.concatenate((main_matrix, funding_gap_matrix), axis=1) # # prepare arguments # bonds_mv_T = np.transpose(bonds_mv) # 200 x 1 # opt_matrix_T = np.transpose(opt_matrix) # 47 x 200 # bounds_vector_T = np.transpose(bounds_vector) # 47 x 1 # #bounds_vector_T = np.transpose(funding_gap_bounds) #Model solver: model = LpProblem(name="BondPortfolio", sense=LpMaximize) x = {i: LpVariable(name=f"x{i}", lowBound=0) for i in range(200)} # objective: model += liabilities_NPV - lpSum(cost_vector[i] * x[i] for i in range(200)) # funding gap constraints for i in range(20): model += lpSum(x[j] * main_matrix[j, i] for j in range(200)) <= funding_gap_upper[i] model += lpSum(x[j] * main_matrix[j, i] for j in range(200)) >= funding_gap_lower[i] status = model.solve() print(f"status: {model.status}, {LpStatus[model.status]}") print(f"objective: {model.objective.value()}")
def __init__(self): self.prob = LpProblem('Daily Fantasy Sports', LpMaximize)
LOAN2 = 80 * 1000 MONTHLY_INTEREST2 = 0.08 / 12.0 ANNUAL_SALARY = 109 * 1000 TAX_RATE = 0.30 SAVINGS_RATE = 0.50 MONTHLY_CONTRIBUTIONS = (SAVINGS_RATE * ANNUAL_SALARY * (1 - TAX_RATE)) / 12.0 MONTHLY_COMPOUNDING = 0.07 / 12.0 print(MONTHLY_CONTRIBUTIONS) NUM_MONTHS = 10 * 12 # Define the model model = LpProblem(name="loan_optimization", sense=LpMaximize) # Define the decision variables loan1 = \ {i: LpVariable(name=f"l1_{i}", lowBound=0) for i in range(NUM_MONTHS)} loan_contribution1 = \ {i: LpVariable(name=f"lc1_{i}", lowBound=0) for i in range(NUM_MONTHS)} interest1 = \ {i: LpVariable(name=f"interest1_{i}", lowBound=0) for i in range(NUM_MONTHS)} loan2 = \ {i: LpVariable(name=f"l2_{i}", lowBound=0) for i in range(NUM_MONTHS)} loan_contribution2 = \ {i: LpVariable(name=f"lc2_{i}", lowBound=0) for i in range(NUM_MONTHS)} interest2 = \ {i: LpVariable(name=f"interest2_{i}", lowBound=0) for i in range(NUM_MONTHS)}
def main(): logger.info("start") n_family = 5000 n_days = 100 n_choice = 10 days = list(range(n_days, 0, -1)) df_family = pd.read_csv("../input/family_data.csv", index_col="family_id") map_family_size = df_family["n_people"].to_dict() with open("mat_cost.pkl", "rb") as f: mat_cost = pickle.load(f) map_var_x_cost = {(f, d): mat_cost[f, d - 1] for f in range(n_family) for d in days} # map_var_x_cost[f, d] family i, day d map_cost_ac = {} for i in tqdm(range(125, 301)): for j in range(125, 301): diff = abs(i - j) map_cost_ac[i, j] = min( max(0, (i - 125.0) / 400.0 * i ** (0.5 + diff / 50.0)), 100000 ) map_var_x_name = {(i, j): f"x_{i}_{j}" for i in range(n_family) for j in days} map_var_z_cost = { (p, q, d): map_cost_ac[p, q] for p in range(125, 301) for q in range(125, 301) for d in days } map_var_z_name = { (p, q, d): f"z_{p}_{q}_{d}" for p in range(125, 301) for q in range(125, 301) for d in days } model = LpProblem("santa2019", LpMinimize) logger.info("Define vars") var_x = {k: LpVariable(f"x_{k}", cat=LpBinary) for k in map_var_x_cost} var_z = {k: LpVariable(f"z_{k}", cat=LpBinary) for k in map_var_z_cost} logger.info("Each familiy must attend 1 session") for f in range(n_family): model += lpSum([var_x[f, d] for d in days]) == 1 logger.info("Each day has 125-300 people") for d in tqdm(days): model += ( lpSum(var_z[p, q, d] for p in range(125, 301) for q in range(125, 301)) == 1 ) logger.info("Knapsack constraints") for d in tqdm(days): model += lpSum( map_family_size[f] * var_x[f, d] for f in range(n_family) ) == lpSum(p * var_z[p, q, d] for p in range(125, 301) for q in range(125, 301)) logger.info("Day-by-day constraints") for d in tqdm(days): for q in range(125, 301): model += lpSum(var_z[p, q, d] for p in range(125, 301)) == lpSum( var_z[q, p, min(d + 1, 100)] for p in range(125, 301) ) logger.info("Objective function") model += lpSum(v * var_x[k] for k, v in map_var_x_cost.items()) + lpSum( v * var_z[k] for k, v in map_var_z_cost.items() ) logger.info("Output LP file") model.writeLP("santa2019_pulp.lp") logger.info("optimization starts") solver = COIN_CMD(presolve=1, threads=8, maxSeconds=3600, msg=1) model.solve(solver)
from pulp import LpMaximize, LpProblem, LpStatus, lpSum, LpVariable, PULP_CBC_CMD import numpy as np # Store the key figures hardware = [ "Notebook Büro 13\"", "Notebook Büro 14\"", "Notebook outdoor", "Mobiltelefon Büro", "Mobiltelefon Outdoor", "Mobiltelefon Heavy Duty", "Tablet Büro klein", "Tablet Büro groß", "Tablet outdoor klein", "Tablet outdoor groß" ] max_amount = [205, 420, 450, 60, 157, 220, 620, 250, 540, 370] weights = [2451, 2978, 3625, 717, 988, 1220, 1405, 1455, 1690, 1980] benefits = [40, 35, 80, 30, 60, 65, 40, 40, 45, 68] # Create the model model = LpProblem(name="opt-transport", sense=LpMaximize) # Initialize the decision variables x = list({ i: LpVariable(name=f"x{i}", lowBound=0, cat="Integer") for i in range(1, 21) }.values()) # Add the constraints to the model total_weight1 = np.dot(weights, x[:10]) total_weight2 = np.dot(weights, x[10:]) model += (total_weight1 <= 1100000 - 72400, "transporter1" ) # Upper limit for the loading weight of the first transporter model += (total_weight2 <= 1100000 - 85700, "transporter2" ) # Upper limit for the loading weight of the second transporter for i in range(10):
def _initialize_model(self): self.mdl = LpProblem("MultiTerminalCuts", LpMinimize)
from pulp import LpMaximize, LpProblem, LpStatus, lpSum, LpVariable # Problem #1 # minimize z = x + 2y # st 2x + y <= 20 (red) # -4x + 5y <= 10 (blue) # -x + 2y >= -2 (yellow) # -x + 5y = 15 (green) # x >= 0 # y >= 0 # create the model model = LpProblem(name='small-problem', sense=LpMaximize) # initialize decision variables # default is negative infinity to positive infinity # can set upper bound with upBound # can specify category with cat='Continuous', 'Integer', 'Binary' x = LpVariable(name='x', lowBound=0) y = LpVariable(name='y', lowBound=0) # now that we have create the decision variables x and y, we can use them # to create other PuLP objects expression = 2*x + 4*y type(expression) constraint = 2*x + 4*y >= 8 type(constraint)
bb = [] for i in range(15): bb.append(1) c = np.array(aa) A_ub = np.array(judge) # 不等式约束 b_ub = np.array(bb) # A_eq = np.array(select) # 等式约束 # b_eq = np.array(aa) r = linprog(c, A_ub=-A_ub, b_ub=-b_ub, bounds=( (0, 1), (0, 1), (0, 1), (0, 1), (0, 1), (0, 1), (0, 1), (0, 1), (0, 1), (0, 1), (0, 1), (0, 1), (0, 1), (0, 1), (0, 1), (0, 1), (0, 1), (0, 1), (0, 1), (0, 1), (0, 1), (0, 1), (0, 1))) print(r) ''' # Create a new model m = LpProblem(name="MIP Model", sense=LpMinimize) # Create variables name_list = [ 'x1', 'x2', 'x3', 'x4', 'x5', 'x6', 'x7', 'x8', 'x9', 'x10', 'x11', 'x12',
def solve(self, nets, parameters): """ Resuelve el TNEP, devuelve un listado de PFNET Networks con la solucion parameters es un objeto del tipo Parameters """ options = self.options # Create useful data structure ds = self._create_data_structure(nets, parameters) # Instanciate problem prob = LpProblem("TLEP", LpMinimize) # Variables w = {(i, bus_i): LpVariable(f'w_{i}_{bus_i}') for (i, bus_i) in ds["ref"]["bus"]} r = {(i, bus_i): LpVariable(f'r_{i}_{bus_i}', lowBound=0) for (i, bus_i) in ds["ref"]["bus"]} pg = {(i, bus_i, name): LpVariable(f'ps_{i}_{bus_i}_{name}') for (i, bus_i, name) in ds["ref"]["gen"]} f = {(i, k, m, ckt): LpVariable(f'f_{i, k, m, ckt}') for (i, k, m, ckt) in ds["ref"]["arcs"]} f_ = {(i, k, m, ckt): LpVariable(f'f_vio_{i, k, m, ckt}') for (i, k, m, ckt) in list(set(ds["ref"]["ne_arcs"] + ds["ref"]["mo_arcs"]))} phi_ = {(i, k, m, ckt): LpVariable(f'phi_{i, k, m, ckt}') for (i, k, m, ckt) in ds["ref"]["ne_arcs"]} x = {(k, m, ckt): LpVariable(f'x{k, m, ckt}', cat='Integer', lowBound=0, upBound=1) for (k, m, ckt) in ds["ref"]["ne_arc"]} # Objective prob += (sum(arc['cost'] * x[index] for (index, arc) in ds["ne_br"].items()) + options['penalty'] * sum(vio for vio in f_.values()) + options['ens'] * sum(ds['c_k'][i[0]] * ds['crf'] * l_shed for i, l_shed in r.items())) # Constraints for i, net in ds["nets"].items(): for bus in net.buses: # Power Balance if not bus.is_in_service(): continue dp = 0.0 for gen in bus.generators: dp += pg[i, bus.number, gen.name] if bus.is_slack() else gen.P # Recorte de carga dp += r[i, bus.number] for load in bus.loads: dp -= load.P # Se podria optimizar esta parte for (j, k, m, ckt) in ds["ref"]["arcs"]: # Arcs es la union de lineas + lineas candidatos if j == i: if k == bus.number: dp -= f[i, k, m, ckt] if m == bus.number: dp += f[i, k, m, ckt] prob += dp == 0 for br in net.branches: # Lineas comunes y a monitorear if not br.is_in_service(): continue k, m, ckt = br.bus_k.number, br.bus_m.number, br.name prob += f[i, k, m, ckt] == -br.b*(w[i, k]-w[i, m]) for bus in net.buses: if not bus.is_in_service(): continue if bus.is_slack(): prob += w[i, bus.number] == 0. prob += r[i, bus.number] <= sum(load.P for load in bus.loads if load.is_in_service()) * ds['load bus'].get(bus.number, 0.) for (i, k, m, ckt) in ds["ref"]["mo_arcs"]: rate = ds["mo_br"][(k, m, ckt)]['rate'] * options['rate factor'] prob += f[i, k, m, ckt] <= rate + f_[i, k, m, ckt] prob += f[i, k, m, ckt] >= -rate - f_[i, k, m, ckt] prob += f_[i, k, m, ckt] >= 0 prob += f_[i, k, m, ckt] <= 1.5 for (i, k, m, ckt) in ds["ref"]["ne_arcs"]: br = ds["ne_br"][(k, m, ckt)]['br'] rate = ds["ne_br"][(k, m, ckt)]['rate'] * options['rate factor'] M = 1e3 prob += f[i, k, m, ckt]+br.b*(w[i, k]-w[i, m]) <= (1-x[k, m, ckt])*M prob += f[i, k, m, ckt]+br.b*(w[i, k]-w[i, m]) >= -(1-x[k, m, ckt])*M prob += f[i, k, m, ckt] <= rate + f_[i, k, m, ckt] prob += f[i, k, m, ckt] >= -rate - f_[i, k, m, ckt] prob += f[i, k, m, ckt] <= x[k, m, ckt]*rate + phi_[i, k, m, ckt] prob += f[i, k, m, ckt] >= -x[k, m, ckt]*rate - phi_[i, k, m, ckt] prob += phi_[i, k, m, ckt] - f_[i, k, m, ckt] <= (1-x[k, m, ckt])*M prob += phi_[i, k, m, ckt] - f_[i, k, m, ckt] >= -(1-x[k, m, ckt])*M prob += phi_[i, k, m, ckt] <= x[k, m, ckt]*M prob += phi_[i, k, m, ckt] >= -x[k, m, ckt]*M prob += f_[i, k, m, ckt] >= 0 prob.solve( PULP_CBC_CMD( mip=True, cuts=False, msg=1, options=['preprocess off presolve on gomoryCuts on'], ) ) # Get solution details ds["solution"] = { 'objective': prob.objective.value(), 'br_builded': sum(var.value() for var in x.values()), 'br_cost': sum(x[index].value() * br['cost'] for (index, br) in ds["ne_br"].items()), 'overload': options['penalty'] * sum(vio.value() for vio in f_.values()), 'r_dem': sum(var.value() for var in r.values()), 'ens': options['ens'] * sum(ds['c_k'][i[0]] * ds['crf'] * l_shed.value() for i, l_shed in r.items()), 'status': prob.status } # Update Branches in Network for net in ds["nets"].values(): build_branches = [] for (k, m, ckt), build in x.items(): if round(build.value()) > 0: ref_br = (ds["ne_br"][(k, m, ckt)]['br']) br = pfnet.Branch() br.bus_k = net.get_bus_from_number(k) br.bus_m = net.get_bus_from_number(m) br.name = ref_br.name br.g = ref_br.g br.b = ref_br.b br.ratingA = ref_br.ratingA br.in_service = br.in_service build_branches.append(br) net.add_branches(build_branches) net.update_properties() # Update load shed for i, i_bus in r.keys(): net = ds["nets"][i] bus = net.get_bus_from_number(i_bus) if sum(l.P for l in bus.loads) > 0: load = bus.loads[0] load.P = load.P - r[i, i_bus].value() load.update_P_components(1, 0, 0) net.update_properties() return list(ds["nets"].values()), ds["solution"]
'calcium': 2, 'vitamin': 10 }, 'b': { 'protein': 45, 'calcium': 1, 'vitamin': 5 } } q = {'protein': 700, 'calcium': 28, 'vitamin': 150} c = {'a': 0.3, 'b': 0.35} #### Problem statement # Create a variable to store problem information myProblem = LpProblem('Diet Problem', LpMinimize) # Create a variable to store decision variables; # they will be named as <name>_<index> by PuLP modeler x = LpVariable.dicts( 'food', # name foods, # array of indexes 0 # lower bound ) # Add objective function (must be added first) myProblem += lpSum([c[i] * x[i] for i in foods]) # Add problem constraints for j in nutrients: myProblem += lpSum([p[i][j] * x[i] for i in foods]) >= q[j] # Note there is no need to include x[i] >= 0 constraint since x has been # created with lower bound equals to zero
[0, 45, 0, 0, 150], [0, 0, 0, 0, 0], ] # Decision variables var: Dict = {} for i in nodes: for j in nodes: if fork[i][j] == 1: var[(i, j)] = LpVariable(name=f"x{i}{j}", cat="Binary") else: continue var.update(var) # Model model = LpProblem("Shortest_path_problem", LpMinimize) # Goal function model += lpSum(var[x] * costs[x[0]][x[1]] for x in var.keys()) # Constrains constrains_o: List = [] constrains_f: List = [] for node in nodes: for edge in var.keys(): if edge[0] == node: constrains_o.append(var[edge]) if edge[1] == node:
random.seed(1) #顧客の集合(顧客は主にjで表す) D_SIZE = 30 #施設候補場所(施設は主にiで表す) F_SIZE = 5 ###問題設定 #施設建設にかかるコスト f = np.random.randint(low=100, high=200, size=F_SIZE) #各施設から顧客への輸送コスト c = np.random.randint(low=10, high=100, size=(F_SIZE, D_SIZE)) p = LpProblem(name="FLP", sense=LpMinimize) #施設iから顧客jに運ぶ製品の割合(どれだけ需要を満たすかの割合) x = LpVariable.dict('x', indexs=(range(F_SIZE), range(D_SIZE)), lowBound=0, upBound=1, cat=LpContinuous) #施設iを開設するか(1)しないか(0)を表す変数y y = LpVariable.dict('y', indexs=(range(F_SIZE)), lowBound=0, upBound=1, cat=LpBinary)
def test_result_type(self): function_result = solve_linear_problem(LpProblem()) self.assertTrue(isinstance(function_result, tuple))
def fit_from_scipy_sparse_matrix(self, adj, attr, spatially_extensive_attr, threshold, solver="cbc", metric="euclidean"): """ Solve the max-p-regions problem as MIP as described in [DAR2012]_. The resulting region labels are assigned to the instance's :attr:`labels_` attribute. Parameters ---------- adj : class:`scipy.sparse.csr_matrix` Adjacency matrix representing the areas' contiguity relation. attr : :class:`numpy.ndarray` Array (number of areas x number of attributes) of areas' attributes relevant to clustering. spatially_extensive_attr : :class:`numpy.ndarray` Array (number of areas x number of attributes) of areas' attributes relevant to ensuring the threshold condition. threshold : numbers.Real or :class:`numpy.ndarray` The lower bound for a region's sum of spatially extensive attributes. The argument's type is numbers.Real if there is only one spatially extensive attribute per area, otherwise it is a one-dimensional array with as many entries as there are spatially extensive attributes per area. solver : {"cbc", "cplex", "glpk", "gurobi"}, default: "cbc" The solver to use. Unless the default solver is used, the user has to make sure that the specified solver is installed. * "cbc" - the Cbc (Coin-or branch and cut) solver * "cplex" - the CPLEX solver * "glpk" - the GLPK (GNU Linear Programming Kit) solver * "gurobi" - the Gurobi Optimizer metric : str or function, default: "euclidean" See the `metric` argument in :func:`region.util.get_metric_function`. """ self.metric = get_metric_function(metric) check_solver(solver) prob = LpProblem("Max-p-Regions", LpMinimize) # Parameters of the optimization problem n_areas = adj.shape[0] I = list(range(n_areas)) # index for areas II = [(i, j) for i in I for j in I] II_upper_triangle = [(i, j) for i, j in II if i < j] # index of potential regions, called k in [DAR2012]_: K = range(n_areas) # index of contiguity order, called c in [DAR2012]_: O = range(n_areas) d = {(i, j): self.metric(attr[i].reshape(1, -1), attr[j].reshape(1, -1)) for i, j in II_upper_triangle} h = 1 + floor(log10(sum(d[(i, j)] for i, j in II_upper_triangle))) # Decision variables t = LpVariable.dicts( "t", ((i, j) for i, j in II_upper_triangle), lowBound=0, upBound=1, cat=LpInteger) x = LpVariable.dicts( "x", ((i, k, o) for i in I for k in K for o in O), lowBound=0, upBound=1, cat=LpInteger) # Objective function # (1) in Duque et al. (2012): "The Max-p-Regions Problem" prob += -10**h * lpSum(x[i, k, 0] for k in K for i in I) \ + lpSum(d[i, j] * t[i, j] for i, j in II_upper_triangle) # Constraints # (2) in Duque et al. (2012): "The Max-p-Regions Problem" for k in K: prob += lpSum(x[i, k, 0] for i in I) <= 1 # (3) in Duque et al. (2012): "The Max-p-Regions Problem" for i in I: prob += lpSum(x[i, k, o] for k in K for o in O) == 1 # (4) in Duque et al. (2012): "The Max-p-Regions Problem" for i in I: for k in K: for o in range(1, len(O)): prob += x[i, k, o] <= lpSum(x[j, k, o-1] for j in neighbors(adj, i)) # (5) in Duque et al. (2012): "The Max-p-Regions Problem" if isinstance(spatially_extensive_attr[I[0]], numbers.Real): for k in K: lhs = lpSum(x[i, k, o] * spatially_extensive_attr[i] for i in I for o in O) prob += lhs >= threshold * lpSum(x[i, k, 0] for i in I) elif isinstance(spatially_extensive_attr[I[0]], collections.Iterable): for el in range(len(spatially_extensive_attr[I[0]])): for k in K: lhs = lpSum(x[i, k, o] * spatially_extensive_attr[i][el] for i in I for o in O) if isinstance(threshold, numbers.Real): rhs = threshold * lpSum(x[i, k, 0] for i in I) prob += lhs >= rhs elif isinstance(threshold, np.ndarray): rhs = threshold[el] * lpSum(x[i, k, 0] for i in I) prob += lhs >= rhs # (6) in Duque et al. (2012): "The Max-p-Regions Problem" for i, j in II_upper_triangle: for k in K: prob += t[i, j] >= \ lpSum(x[i, k, o] + x[j, k, o] for o in O) - 1 # (7) in Duque et al. (2012): "The Max-p-Regions Problem" # already in LpVariable-definition # (8) in Duque et al. (2012): "The Max-p-Regions Problem" # already in LpVariable-definition # additional constraint for speedup (p. 405 in [DAR2012]_) for o in O: prob += x[I[0], K[0], o] == (1 if o == 0 else 0) # Solve the optimization problem solver = get_solver_instance(solver) print("start solving with", solver) prob.solve(solver=solver) print("solved") result = np.zeros(n_areas) for i in I: for k in K: for o in O: if x[i, k, o].varValue == 1: result[i] = k self.labels_ = result self.solver = solver
def test_result_subtypes_1(self): function_result = solve_linear_problem(LpProblem()) self.assertTrue(isinstance(function_result[0], LpProblem)) self.assertTrue(function_result[1] is None) self.assertTrue(isinstance(function_result[2], int))
def solve_integer_programing( reactant_species: List[str], product_species: List[str], reactant_bonds: List[Bond], product_bonds: List[Bond], solver: str = "COIN_CMD", **kwargs, ) -> Tuple[int, List[Union[int, None]], List[Union[int, None]]]: """ Solve an integer programming problem to get atom mapping between reactants and products. This is a utility function for `get_reaction_atom_mapping()`. Args: reactant_species: species string of reactant atoms product_species: species string of product atoms reactant_bonds: bonds in reactant product_bonds: bonds in product solver: pulp solver to solve the integer linear programming problem. Different solver may give different result if there is degeneracy in the problem, e.g. symmetry in molecules. So, to give deterministic result, the default solver is set to `COIN_CMD`. If this solver is unavailable on your machine, try a different one (e.g. PULP_CBC_CMD). For a full list of solvers, see https://coin-or.github.io/pulp/guides/how_to_configure_solvers.html kwargs: additional keyword arguments passed to the pulp solver. Returns: objective: minimized objective value. This corresponds to the number of changed bonds (both broken and formed) in the reaction. r2p_mapping: mapping of reactant atom to product atom, e.g. r2p_mapping[0] giving 3 means that reactant atom 0 maps to product atom 3. A value of `None` means a mapping cannot be found for the reactant atom. p2r_mapping: mapping of product atom to reactant atom, e.g. p2r_mapping[3] giving 0 means that product atom 3 maps to reactant atom 0. A value of `None` means a mapping cannot be found for the product atom. Reference: `Stereochemically Consistent Reaction Mapping and Identification of Multiple Reaction Mechanisms through Integer Linear Optimization`, J. Chem. Inf. Model. 2012, 52, 84–92, https://doi.org/10.1021/ci200351b """ solver = _check_pulp_solver(solver) # default msg to False to avoid printing solver info msg = kwargs.pop("msg", False) solver = pulp.getSolver(solver, msg=msg, **kwargs) atoms = list(range(len(reactant_species))) # init model and variables model = LpProblem(name="Reaction_Atom_Mapping", sense=LpMinimize) y_vars = {(i, k): LpVariable(cat=LpBinary, name=f"y_{i}_{k}") for i in atoms for k in atoms} alpha_vars = {(i, j, k, l): LpVariable(cat=LpBinary, name=f"alpha_{i}_{j}_{k}_{l}") for (i, j) in reactant_bonds for (k, l) in product_bonds} # add constraints # constraint 2: each atom in the reactants maps to exactly one atom in the products # constraint 3: each atom in the products maps to exactly one atom in the reactants for i in atoms: model += lpSum([y_vars[(i, k)] for k in atoms]) == 1 for k in atoms: model += lpSum([y_vars[(i, k)] for i in atoms]) == 1 # constraint 4: allows only atoms of the same type to map to one another for i in atoms: for k in atoms: if reactant_species[i] != product_species[k]: model += y_vars[(i, k)] == 0 # constraints 5 and 6: define each alpha_ijkl variable, permitting it to take the # value of one only if the reactant bond (i,j) maps to the product bond (k,l) for (i, j) in reactant_bonds: for (k, l) in product_bonds: model += alpha_vars[(i, j, k, l)] <= y_vars[(i, k)] + y_vars[(i, l)] model += alpha_vars[(i, j, k, l)] <= y_vars[(j, k)] + y_vars[(j, l)] # create objective obj1 = lpSum(1 - lpSum(alpha_vars[(i, j, k, l)] for (k, l) in product_bonds) for (i, j) in reactant_bonds) obj2 = lpSum(1 - lpSum(alpha_vars[(i, j, k, l)] for (i, j) in reactant_bonds) for (k, l) in product_bonds) obj = obj1 + obj2 # solve the problem try: model.setObjective(obj) model.solve(solver) except Exception: raise ReactionMappingError("Failed solving integer programming.") objective = pulp.value(model.objective) # type: int if objective is None: raise ReactionMappingError("Failed solving integer programming.") # get atom mapping between reactant and product r2p_mapping = [None for _ in atoms] # type: List[Union[int, None]] p2r_mapping = [None for _ in atoms] # type: List[Union[int, None]] for (i, k), v in y_vars.items(): if pulp.value(v) == 1: r2p_mapping[i] = k p2r_mapping[k] = i return objective, r2p_mapping, p2r_mapping
def BranchAndBound(T, CONSTRAINTS, VARIABLES, OBJ, MAT, RHS, branch_strategy=MOST_FRACTIONAL, search_strategy=DEPTH_FIRST, complete_enumeration=False, display_interval=None, binary_vars=True): if T.get_layout() == 'dot2tex': cluster_attrs = { 'name': 'Key', 'label': r'\text{Key}', 'fontsize': '12' } T.add_node('C', label=r'\text{Candidate}', style='filled', color='yellow', fillcolor='yellow') T.add_node('I', label=r'\text{Infeasible}', style='filled', color='orange', fillcolor='orange') T.add_node('S', label=r'\text{Solution}', style='filled', color='lightblue', fillcolor='lightblue') T.add_node('P', label=r'\text{Pruned}', style='filled', color='red', fillcolor='red') T.add_node('PC', label=r'\text{Pruned}$\\ $\text{Candidate}', style='filled', color='red', fillcolor='yellow') else: cluster_attrs = {'name': 'Key', 'label': 'Key', 'fontsize': '12'} T.add_node('C', label='Candidate', style='filled', color='yellow', fillcolor='yellow') T.add_node('I', label='Infeasible', style='filled', color='orange', fillcolor='orange') T.add_node('S', label='Solution', style='filled', color='lightblue', fillcolor='lightblue') T.add_node('P', label='Pruned', style='filled', color='red', fillcolor='red') T.add_node('PC', label='Pruned \n Candidate', style='filled', color='red', fillcolor='yellow') T.add_edge('C', 'I', style='invisible', arrowhead='none') T.add_edge('I', 'S', style='invisible', arrowhead='none') T.add_edge('S', 'P', style='invisible', arrowhead='none') T.add_edge('P', 'PC', style='invisible', arrowhead='none') T.create_cluster(['C', 'I', 'S', 'P', 'PC'], cluster_attrs) # The initial lower bound LB = -INFINITY # The number of LP's solved, and the number of nodes solved node_count = 1 iter_count = 0 lp_count = 0 if binary_vars: var = LpVariable.dicts("", VARIABLES, 0, 1) else: var = LpVariable.dicts("", VARIABLES) numCons = len(CONSTRAINTS) numVars = len(VARIABLES) # List of incumbent solution variable values opt = dict([(i, 0) for i in VARIABLES]) pseudo_u = dict((i, (OBJ[i], 0)) for i in VARIABLES) pseudo_d = dict((i, (OBJ[i], 0)) for i in VARIABLES) print("===========================================") print("Starting Branch and Bound") if branch_strategy == MOST_FRACTIONAL: print("Most fractional variable") elif branch_strategy == FIXED_BRANCHING: print("Fixed order") elif branch_strategy == PSEUDOCOST_BRANCHING: print("Pseudocost brancing") else: print("Unknown branching strategy %s" % branch_strategy) if search_strategy == DEPTH_FIRST: print("Depth first search strategy") elif search_strategy == BEST_FIRST: print("Best first search strategy") else: print("Unknown search strategy %s" % search_strategy) print("===========================================") # List of candidate nodes Q = PriorityQueue() # The current tree depth cur_depth = 0 cur_index = 0 # Timer timer = time.time() Q.push(0, -INFINITY, (0, None, None, None, None, None, None)) # Branch and Bound Loop while not Q.isEmpty(): infeasible = False integer_solution = False (cur_index, parent, relax, branch_var, branch_var_value, sense, rhs) = Q.pop() if cur_index is not 0: cur_depth = T.get_node_attr(parent, 'level') + 1 else: cur_depth = 0 print("") print("----------------------------------------------------") print("") if LB > -INFINITY: print("Node: %s, Depth: %s, LB: %s" % (cur_index, cur_depth, LB)) else: print("Node: %s, Depth: %s, LB: %s" % (cur_index, cur_depth, "None")) if relax is not None and relax <= LB: print("Node pruned immediately by bound") T.set_node_attr(parent, 'color', 'red') continue # ==================================== # LP Relaxation # ==================================== # Compute lower bound by LP relaxation prob = LpProblem("relax", LpMaximize) prob += lpSum([OBJ[i] * var[i] for i in VARIABLES]), "Objective" for j in range(numCons): prob += (lpSum([MAT[i][j] * var[i] for i in VARIABLES]) <= RHS[j], CONSTRAINTS[j]) # Fix all prescribed variables branch_vars = [] if cur_index is not 0: sys.stdout.write("Branching variables: ") branch_vars.append(branch_var) if sense == '>=': prob += LpConstraint(lpSum(var[branch_var]) >= rhs) else: prob += LpConstraint(lpSum(var[branch_var]) <= rhs) print(branch_var, end=' ') pred = parent while not str(pred) == '0': pred_branch_var = T.get_node_attr(pred, 'branch_var') pred_rhs = T.get_node_attr(pred, 'rhs') pred_sense = T.get_node_attr(pred, 'sense') if pred_sense == '<=': prob += LpConstraint( lpSum(var[pred_branch_var]) <= pred_rhs) else: prob += LpConstraint( lpSum(var[pred_branch_var]) >= pred_rhs) print(pred_branch_var, end=' ') branch_vars.append(pred_branch_var) pred = T.get_node_attr(pred, 'parent') print() # Solve the LP relaxation prob.solve() lp_count = lp_count + 1 # Check infeasibility infeasible = LpStatus[prob.status] == "Infeasible" or \ LpStatus[prob.status] == "Undefined" # Print status if infeasible: print("LP Solved, status: Infeasible") else: print("LP Solved, status: %s, obj: %s" % (LpStatus[prob.status], value(prob.objective))) if (LpStatus[prob.status] == "Optimal"): relax = value(prob.objective) # Update pseudocost if branch_var != None: if sense == '<=': pseudo_d[branch_var] = (old_div( (pseudo_d[branch_var][0] * pseudo_d[branch_var][1] + old_div((T.get_node_attr(parent, 'obj') - relax), (branch_var_value - rhs))), (pseudo_d[branch_var][1] + 1)), pseudo_d[branch_var][1] + 1) else: pseudo_u[branch_var] = (old_div( (pseudo_u[branch_var][0] * pseudo_d[branch_var][1] + old_div((T.get_node_attr(parent, 'obj') - relax), (rhs - branch_var_value))), (pseudo_u[branch_var][1] + 1)), pseudo_u[branch_var][1] + 1) var_values = dict([(i, var[i].varValue) for i in VARIABLES]) integer_solution = 1 for i in VARIABLES: if (abs(round(var_values[i]) - var_values[i]) > .001): integer_solution = 0 break # Determine integer_infeasibility_count and # Integer_infeasibility_sum for scatterplot and such integer_infeasibility_count = 0 integer_infeasibility_sum = 0.0 for i in VARIABLES: if (var_values[i] not in set([0, 1])): integer_infeasibility_count += 1 integer_infeasibility_sum += min( [var_values[i], 1.0 - var_values[i]]) if (integer_solution and relax > LB): LB = relax for i in VARIABLES: # These two have different data structures first one # list, second one dictionary opt[i] = var_values[i] print("New best solution found, objective: %s" % relax) for i in VARIABLES: if var_values[i] > 0: print("%s = %s" % (i, var_values[i])) elif (integer_solution and relax <= LB): print("New integer solution found, objective: %s" % relax) for i in VARIABLES: if var_values[i] > 0: print("%s = %s" % (i, var_values[i])) else: print("Fractional solution:") for i in VARIABLES: if var_values[i] > 0: print("%s = %s" % (i, var_values[i])) # For complete enumeration if complete_enumeration: relax = LB - 1 else: relax = INFINITY if integer_solution: print("Integer solution") BBstatus = 'S' status = 'integer' color = 'lightblue' elif infeasible: print("Infeasible node") BBstatus = 'I' status = 'infeasible' color = 'orange' elif not complete_enumeration and relax <= LB: print("Node pruned by bound (obj: %s, UB: %s)" % (relax, LB)) BBstatus = 'P' status = 'fathomed' color = 'red' elif cur_depth >= numVars: print("Reached a leaf") BBstatus = 'fathomed' status = 'L' else: BBstatus = 'C' status = 'candidate' color = 'yellow' if BBstatus is 'I': if T.get_layout() == 'dot2tex': label = '\text{I}' else: label = 'I' else: label = "%.1f" % relax if iter_count == 0: if status is not 'candidate': integer_infeasibility_count = None integer_infeasibility_sum = None if status is 'fathomed': if T._incumbent_value is None: print('WARNING: Encountered "fathom" line before ' + 'first incumbent.') T.AddOrUpdateNode(0, None, None, 'candidate', relax, integer_infeasibility_count, integer_infeasibility_sum, label=label, obj=relax, color=color, style='filled', fillcolor=color) if status is 'integer': T._previous_incumbent_value = T._incumbent_value T._incumbent_value = relax T._incumbent_parent = -1 T._new_integer_solution = True # #Currently broken # if ETREE_INSTALLED and T.attr['display'] == 'svg': # T.write_as_svg(filename = "node%d" % iter_count, # nextfile = "node%d" % (iter_count + 1), # highlight = cur_index) else: _direction = {'<=': 'L', '>=': 'R'} if status is 'infeasible': integer_infeasibility_count = T.get_node_attr( parent, 'integer_infeasibility_count') integer_infeasibility_sum = T.get_node_attr( parent, 'integer_infeasibility_sum') relax = T.get_node_attr(parent, 'lp_bound') elif status is 'fathomed': if T._incumbent_value is None: print('WARNING: Encountered "fathom" line before' + ' first incumbent.') print(' This may indicate an error in the input file.') elif status is 'integer': integer_infeasibility_count = None integer_infeasibility_sum = None T.AddOrUpdateNode(cur_index, parent, _direction[sense], status, relax, integer_infeasibility_count, integer_infeasibility_sum, branch_var=branch_var, branch_var_value=var_values[branch_var], sense=sense, rhs=rhs, obj=relax, color=color, style='filled', label=label, fillcolor=color) if status is 'integer': T._previous_incumbent_value = T._incumbent_value T._incumbent_value = relax T._incumbent_parent = parent T._new_integer_solution = True # Currently Broken # if ETREE_INSTALLED and T.attr['display'] == 'svg': # T.write_as_svg(filename = "node%d" % iter_count, # prevfile = "node%d" % (iter_count - 1), # nextfile = "node%d" % (iter_count + 1), # highlight = cur_index) if T.get_layout() == 'dot2tex': _dot2tex_label = {'>=': ' \geq ', '<=': ' \leq '} T.set_edge_attr( parent, cur_index, 'label', str(branch_var) + _dot2tex_label[sense] + str(rhs)) else: T.set_edge_attr(parent, cur_index, 'label', str(branch_var) + sense + str(rhs)) iter_count += 1 if BBstatus == 'C': # Branching: # Choose a variable for branching branching_var = None if branch_strategy == FIXED_BRANCHING: # fixed order for i in VARIABLES: frac = min(var[i].varValue - math.floor(var[i].varValue), math.ceil(var[i].varValue) - var[i].varValue) if (frac > 0): min_frac = frac branching_var = i # TODO(aykut): understand this break break elif branch_strategy == MOST_FRACTIONAL: # most fractional variable min_frac = -1 for i in VARIABLES: frac = min(var[i].varValue - math.floor(var[i].varValue), math.ceil(var[i].varValue) - var[i].varValue) if (frac > min_frac): min_frac = frac branching_var = i elif branch_strategy == PSEUDOCOST_BRANCHING: scores = {} for i in VARIABLES: # find the fractional solutions if (var[i].varValue - math.floor(var[i].varValue)) != 0: scores[i] = min(pseudo_u[i][0] * (1 - var[i].varValue), pseudo_d[i][0] * var[i].varValue) # sort the dictionary by value branching_var = sorted(list(scores.items()), key=lambda x: x[1])[-1][0] else: print("Unknown branching strategy %s" % branch_strategy) exit() if branching_var is not None: print("Branching on variable %s" % branching_var) # Create new nodes if search_strategy == DEPTH_FIRST: priority = (-cur_depth - 1, -cur_depth - 1) elif search_strategy == BEST_FIRST: priority = (-relax, -relax) elif search_strategy == BEST_ESTIMATE: priority = (-relax - pseudo_d[branching_var][0] * (math.floor(var[branching_var].varValue) - var[branching_var].varValue), -relax + pseudo_u[branching_var][0] * (math.ceil(var[branching_var].varValue) - var[branching_var].varValue)) node_count += 1 Q.push(node_count, priority[0], (node_count, cur_index, relax, branching_var, var_values[branching_var], '<=', math.floor(var[branching_var].varValue))) node_count += 1 Q.push(node_count, priority[1], (node_count, cur_index, relax, branching_var, var_values[branching_var], '>=', math.ceil(var[branching_var].varValue))) T.set_node_attr(cur_index, color, 'green') if T.root is not None and display_interval is not None and\ iter_count % display_interval == 0: T.display(count=iter_count) timer = int(math.ceil((time.time() - timer) * 1000)) print("") print("===========================================") print("Branch and bound completed in %sms" % timer) print("Strategy: %s" % branch_strategy) if complete_enumeration: print("Complete enumeration") print("%s nodes visited " % node_count) print("%s LP's solved" % lp_count) print("===========================================") print("Optimal solution") # print optimal solution for i in sorted(VARIABLES): if opt[i] > 0: print("%s = %s" % (i, opt[i])) print("Objective function value") print(LB) print("===========================================") if T.attr['display'] is not 'off': T.display(count=iter_count) T._lp_count = lp_count return opt, LB
def solve_sudoku(initial_value_constraints: InitialValueConstraints): # --- set up Sudoku problem # The Vals, Rows and Cols sequences all follow this form Vals = [1,2,3,4,5,6,7,8,9] Rows = [0,1,2,3,4,5,6,7,8] Cols = [0,1,2,3,4,5,6,7,8] # The boxes list is created, with the row and column index of each square in each box Boxes =[] for i in range(3): for j in range(3): Boxes += [[(Rows[3*i+k],Cols[3*j+l]) for k in range(3) for l in range(3)]] # The prob variable is created to contain the problem data prob = LpProblem("Sudoku Problem",LpMinimize) # The problem variables are created choices = LpVariable.dicts("Choice",(Vals,Rows,Cols),0,1,LpInteger) # The arbitrary objective function is added prob += 0, "Arbitrary Objective Function" # A constraint ensuring that only one value can be in each square is created for r in Rows: for c in Cols: prob += lpSum([choices[v][r][c] for v in Vals]) == 1, "" # The row, column and box constraints are added for each value for v in Vals: for r in Rows: prob += lpSum([choices[v][r][c] for c in Cols]) == 1,"" for c in Cols: prob += lpSum([choices[v][r][c] for r in Rows]) == 1,"" for b in Boxes: prob += lpSum([choices[v][r][c] for (r,c) in b]) == 1,"" # add initial values if needed if initial_value_constraints != None and len(initial_value_constraints.initial_values) != 0: for initial_value_constraint in initial_value_constraints.initial_values: prob += choices[initial_value_constraint.value][initial_value_constraint.row_index][initial_value_constraint.column_index] == 1,"" # --- solve sudoku with linear programing solution_status = prob.solve() if solution_status == -1: return SudokuSolution(solved = solution_status) elif solution_status == 1: solved_grid = [[None]*9 for i in range(9)] for row in Rows: for column in Cols: for val in Vals: if value(choices[val][row][column]) == 1: solved_grid[row][column] = val return SudokuSolution(solved = solution_status, solution = solved_grid)
res1 = p.dot(a).dot(Q.transpose()) print(res1) res2 = p.dot(a).dot(Q1.transpose()) print(res2) data3 = pd.DataFrame([[-4, -5, -1, 6], [-1, 0, -3, 5], [-3, 1, -5, 5], [-8, -7, -6, 0]], columns=['B1', 'B2', 'B3', 'B4'], index=['A1', 'A2', 'A3', 'A4']) k = 1000 for i in range(4): g = data3['B' + str(i + 1)].sum() if k > g: k = g f = i + 1 data3.drop(columns='B3') model = LpProblem(name="small-problem", sense=LpMinimize) x = {i: LpVariable(name=f"x{i}", lowBound=0) for i in range(1, 5)} model += ( data3.loc['A1'][0] * x[1] + data3.loc['A2'][0] * x[2] + data3.loc['A3'][0] * x[3] + data3.loc['A4'][0] * x[4] >= -1, "A1") model += ( data3.loc['A1'][1] * x[1] + data3.loc['A2'][1] * x[2] + data3.loc['A3'][1] * x[3] + data3.loc['A4'][1] * x[4] >= -1, "A2") model += ( data3.loc['A1'][2] * x[1] + data3.loc['A2'][2] * x[2] + data3.loc['A3'][2] * x[3] + data3.loc['A4'][2] * x[4] >= -1, "A3") model += -x[1] - x[2] - x[3] - x[4] status = model.solve()
# -*- coding: utf-8 -*- """ Spyder Editor This is a temporary script file. """ from pulp import LpProblem, LpMaximize, LpVariable # Initialize Class model = LpProblem("Maximize Bakery Profits", LpMaximize) # Define Decision Variables A = LpVariable('A', lowBound=0, cat='Integer') B = LpVariable('B', lowBound=0, cat='Integer') # Define Objective Function model += 20 * A + 40 * B # Define Constraints model += 0.5 * A + 1 * B <= 30 model += 1 * A + 2.5 * B <= 60 model += 1 * A + 2 * B <= 22 # Solve Model model.solve() print("Produce {} Cake A".format(A.varValue)) print("Produce {} Cake B".format(B.varValue))
from pulp import LpProblem, LpVariable, LpMaximize, LpStatus, LpInteger, LpAffineExpression if __name__ == "__main__": #LpMaximize means maximize the objective problem = LpProblem("counting to seven", LpMaximize) objects = {"stele" : (20, 400),"codex" : (10, 3000),"manuscript" : (5, 2900),"tablet" : (3, 50),"slab" : (2, 10),"plaque" : (8, 150) } exc1 = False exc2 = True #we declare a new variable named "counter" with a lower limit of 0, #upper limit of 10, and type Integer varTab = {} for obj in objects: varTab[obj] = LpVariable(obj, 0, 1, LpInteger) #var1 = LpVariable("counter", 0, 10, LpInteger) #the first line added to the problem is the objective objective = [] for obj, (wt, chs) in objects.items(): print varTab[obj], chs objective.append( (varTab[obj], chs) ) problem += LpAffineExpression(objective) constraint = [] for obj, (wt, chs) in objects.items(): constraint.append( (varTab[obj], wt) ) problem += ( LpAffineExpression(constraint) <= 25) if exc1: problem += varTab["manuscript"] + varTab["codex"] <= 1
def runtimetable_with_rooms_two_step( STUDENTS, SUBJECTS, TIMES, day, DAYS, TEACHERS, SUBJECTMAPPING, REPEATS, TEACHERMAPPING, TUTORAVAILABILITY, maxclasssize, minclasssize, ROOMS, PROJECTORS, PROJECTORROOMS, numroomsprojector, NONPREFERREDTIMES, CAPACITIES): ''' Run the timetabling process and input into the database. This process calls the CBCSolver using the PuLP package and then adds the classes to the database. :param STUDENTS: should be an array of student names :param SUBJECTS: should be an array of subject codes :param TIMES: an array of strings representing possible timeslots :param day: :param DAYS: the days corresponding to the timeslots above :param TEACHERS: an array of the names of the tutors :param SUBJECTMAPPING: This is a dictionary representing the subjects each tutor is taking :param REPEATS: A dictionary of how many repeats each subject has :param TEACHERMAPPING: A dictionary of what subject each tutor teachers :param TUTORAVAILABILITY: :param maxclasssize: An integer representing the maximum class size :param minclasssize: An integer representing the minimum class size :param nrooms: An integer representing the max allowable concurrent classes :param CAPACITIES: A dictionary indexed by room name with the amount of people that each room can contain :return: A string representing model status. ''' print("Running solver") model = LpProblem('Timetabling', LpMinimize) # Create Variables print("Creating Variables") app.logger.info('Assignment Variables') assign_vars = LpVariable.dicts("StudentVariables", [(i, j, k, m) for m in TEACHERS for j in TEACHERMAPPING[m] for i in SUBJECTMAPPING[j] for k in TIMES], 0, 1, LpBinary) app.logger.info('Subject Variables') subject_vars = LpVariable.dicts("SubjectVariables", [(j, k, m) for m in TEACHERS for j in TEACHERMAPPING[m] for k in TIMES], 0, 1, LpBinary) # c app.logger.info('9:30 classes') num930classes = LpVariable.dicts("930Classes", [(i) for i in TIMES], lowBound=0, cat=LpInteger) # w app.logger.info('Days for teachers') daysforteachers = LpVariable.dicts("numdaysforteachers", [(i, j) for i in TEACHERS for j in range(len(DAYS))], 0, 1, LpBinary) # p daysforteacherssum = LpVariable.dicts("numdaysforteacherssum", [(i) for i in TEACHERS], 0, cat=LpInteger) # variables for student clashes studenttime = LpVariable.dicts("StudentTime", [(i, j) for i in STUDENTS for j in TIMES], lowBound=0, upBound=1, cat=LpBinary) studentsum = LpVariable.dicts("StudentSum", [(i) for i in STUDENTS], 0, cat=LpInteger) projectortime = LpVariable.dicts("ProjectorSum", [(k) for k in TIMES], cat=LpInteger) projectorpositive = LpVariable.dicts("ProjectorPositivePart", [(k) for k in TIMES], 0, cat=LpInteger) # Count the days that a teacher is rostered on. Make it bigger than a small number times the sum # for that particular day. for m in TEACHERS: app.logger.info('Counting Teachers for ' + m) for d in range(len(day)): model += daysforteachers[( m, d)] >= 0.1 * lpSum(subject_vars[(j, k, m)] for j in TEACHERMAPPING[m] for k in DAYS[day[d]]) model += daysforteachers[(m, d)] <= lpSum( subject_vars[(j, k, m)] for j in TEACHERMAPPING[m] for k in DAYS[day[d]]) for m in TEACHERS: model += daysforteacherssum[(m)] == lpSum(daysforteachers[(m, d)] for d in range(len(day))) print("Constraining tutor availability") # This bit of code puts in the constraints for the tutor availability. # It reads in the 0-1 matrix of tutor availability and constrains that no classes # can be scheduled when a tutor is not available. # The last column of the availabilities is the tutor identifying number, hence why we have # used a somewhat convoluted idea down here. for m in TEACHERS: for k in TIMES: if k not in TUTORAVAILABILITY[m]: model += lpSum(subject_vars[(j, k, m)] for j in TEACHERMAPPING[m]) == 0 # Constraints on subjects for each students print("Constraining student subjects") for m in TEACHERS: for j in TEACHERMAPPING[m]: for i in SUBJECTMAPPING[j]: model += lpSum(assign_vars[(i, j, k, m)] for k in TIMES) == 1 # This code means that students cannot attend a tute when a tute is not running # But can not attend a tute if they attend a repeat. for m in TEACHERS: for j in TEACHERMAPPING[m]: for i in SUBJECTMAPPING[j]: for k in TIMES: model += assign_vars[(i, j, k, m)] <= subject_vars[(j, k, m)] # Constraints on which tutor can take each class # This goes through each list and either constrains it to 1 or 0 depending if # the teacher needs to teach that particular class. print("Constraining tutor classes") for m in TEACHERS: for j in TEACHERMAPPING[m]: model += lpSum(subject_vars[(j, k, m)] for k in TIMES) == REPEATS[j] # General Constraints on Rooms etc. print("Constraining times") # For each time cannot exceed number of rooms for k in TIMES: model += lpSum(subject_vars[(j, k, m)] for m in TEACHERS for j in TEACHERMAPPING[m]) <= len(ROOMS) #Number of rooms that need projectors less than the number that have projectors. Want to make this a soft constraint so #that it will not affect the feasibility of the model. We'll have a large penalty for exceeding the number of rooms with #projectors. for k in TIMES: model += projectortime[( k)] == (lpSum(subject_vars[(j, k, m)] for m in TEACHERS for j in TEACHERMAPPING[m] if j in PROJECTORS) - numroomsprojector) model += projectorpositive[(k)] >= projectortime[(k)] model += projectorpositive[(k)] >= 0 # Teachers can only teach one class at a time for k in TIMES: for m in TEACHERS: model += lpSum(subject_vars[(j, k, m)] for j in TEACHERMAPPING[m]) <= 1 print("Constraint: Minimize student clashes") # STUDENT CLASHES for i in STUDENTS: for k in TIMES: model += studenttime[(i, k)] <= lpSum( assign_vars[(i, j, k, m)] for m in TEACHERS for j in TEACHERMAPPING[m] if i in SUBJECTMAPPING[j]) / 2 model += studenttime[(i, k)] >= 0.3 * (0.5 * lpSum( assign_vars[(i, j, k, m)] for m in TEACHERS for j in TEACHERMAPPING[m] if i in SUBJECTMAPPING[j]) - 0.5) for i in STUDENTS: model += studentsum[(i)] == lpSum(studenttime[(i, k)] for k in TIMES) # This minimizes the number of 9:30 classes. for i in TIMES: if i in NONPREFERREDTIMES: model += num930classes[(i)] == lpSum(subject_vars[(j, i, m)] for m in TEACHERS for j in TEACHERMAPPING[m]) else: model += num930classes[(i)] == 0 print("Setting objective function") # Class size constraint for m in TEACHERS: for j in TEACHERMAPPING[m]: for k in TIMES: model += lpSum( assign_vars[(i, j, k, m)] for i in SUBJECTMAPPING[j]) >= minclasssize * subject_vars[ (j, k, m)] model += lpSum(assign_vars[(i, j, k, m)] for i in SUBJECTMAPPING[j]) <= maxclasssize # Solving the model model += (100 * lpSum(studentsum[(i)] for i in STUDENTS) + lpSum(num930classes[(i)] for i in TIMES) + 500 * lpSum(daysforteacherssum[(m)] for m in TEACHERS) + 5000 * lpSum(projectorpositive[(k)] for k in TIMES)) print("Solving Model") model.solve() print("Status:", LpStatus[model.status]) print("Completed Timetable") classpop = {} for m in TEACHERS: for j in TEACHERMAPPING[m]: for k in TIMES: if subject_vars[(j, k, m)].varValue == 1: classpop[(j, k, m)] = sum(assign_vars[(i, j, k, m)].varValue for i in SUBJECTMAPPING[j]) if LpStatus[model.status] == "Optimal": print("Allocating Rooms") model2 = LpProblem('RoomAllocation', LpMinimize) print("Defining Variables") subject_vars_rooms = LpVariable.dicts( "SubjectVariablesRooms", [(j, k, m, n) for m in TEACHERS for j in TEACHERMAPPING[m] for k in TIMES for n in ROOMS if (j, k, m) in classpop.keys()], 0, 1, LpBinary) teacher_number_rooms = LpVariable.dicts("NumberRoomsTeacher", [(m, n) for m in TEACHERS for n in ROOMS], 0, 1, LpBinary) teacher_number_rooms_sum = LpVariable.dicts("NumberRoomsTeacherSum", [(m) for m in TEACHERS], 0) projector_rooms_sum = LpVariable.dicts("ProjectorRooms", [(j) for j in PROJECTORS]) populationovershoot = LpVariable.dicts("PopulationOvershoot", [(k, n) for k in TIMES for n in ROOMS]) poppositive = LpVariable.dicts("PopulationPositivePart", [(k, n) for k in TIMES for n in ROOMS]) print("Minimizing number of rooms for each tutor") for m in TEACHERS: for n in ROOMS: model2 += teacher_number_rooms[(m, n)] >= 0.01 * lpSum( subject_vars_rooms[(j, k, m, n)] for j in TEACHERMAPPING[m] for k in TIMES if (j, k, m) in classpop.keys()) model2 += teacher_number_rooms[(m, n)] <= lpSum( subject_vars_rooms[(j, k, m, n)] for j in TEACHERMAPPING[m] for k in TIMES if (j, k, m) in classpop.keys()) for m in TEACHERS: model2 += teacher_number_rooms_sum[(m)] == lpSum( teacher_number_rooms[(m, n)] for n in ROOMS) # Rooms must be allocated at times when the classes are running print("Constraining Times") for m in TEACHERS: for j in TEACHERMAPPING[m]: for k in TIMES: if (j, k, m) in classpop.keys(): model2 += lpSum( subject_vars_rooms[(j, k, m, n)] for n in ROOMS) == subject_vars[(j, k, m)].varValue for m in TEACHERS: for j in TEACHERMAPPING[m]: if j in PROJECTORS: model2 += projector_rooms_sum[(j)] == lpSum( subject_vars_rooms[(j, k, m, n)] for n in PROJECTORROOMS for k in TIMES if (j, k, m) in classpop.keys()) print("Ensuring Uniqueness") # Can only have one class in each room at a time. for k in TIMES: for n in ROOMS: model2 += lpSum(subject_vars_rooms[(j, k, m, n)] for m in TEACHERS for j in TEACHERMAPPING[m] if (j, k, m) in classpop.keys()) <= 1 print("Accomodating Capacities") for k in TIMES: for n in ROOMS: model2 += populationovershoot[(k, n)] == (lpSum( classpop[(j, k, m)] * subject_vars_rooms[(j, k, m, n)] for m in TEACHERS for j in TEACHERMAPPING[m] if (j, k, m) in classpop.keys()) - CAPACITIES[n]) #print("Second") model2 += poppositive[(k, n)] >= populationovershoot[(k, n)] model2 += poppositive[(k, n)] >= 0 print("Setting Objective Function") model2 += lpSum( teacher_number_rooms_sum[(m)] for m in TEACHERS) - 50 * lpSum(projector_rooms_sum[ (j)] for j in PROJECTORS) + 10 * lpSum(poppositive[(k, n)] for k in TIMES for n in ROOMS) print("Solve Room Allocation") model2.solve() print(LpStatus[model2.status]) if LpStatus[model2.status] == 'Optimal': print("Complete") print("Adding to Database") timetabler.models.add_classes_to_timetable_twostep( TEACHERS, TEACHERMAPPING, SUBJECTMAPPING, TIMES, subject_vars_rooms, assign_vars, ROOMS, classpop) print("Status:", LpStatus[model2.status]) return LpStatus[model.status]