diff --git a/examples/cp/jupyter/golomb_ruler.ipynb b/examples/cp/jupyter/golomb_ruler.ipynb index 76c1ef9..b68caea 100644 --- a/examples/cp/jupyter/golomb_ruler.ipynb +++ b/examples/cp/jupyter/golomb_ruler.ipynb @@ -227,7 +227,7 @@ "outputs": [], "source": [ "# Create model object\n", - "mdl = CpoModel()" + "mdl = CpoModel(name=\"GolombRuler\")" ] }, { diff --git a/examples/cp/jupyter/house_building.ipynb b/examples/cp/jupyter/house_building.ipynb index c86a5a4..d87bf9e 100644 --- a/examples/cp/jupyter/house_building.ipynb +++ b/examples/cp/jupyter/house_building.ipynb @@ -486,7 +486,7 @@ }, "outputs": [], "source": [ - "mdl = CpoModel()" + "mdl = CpoModel(name=\"HouseBuilding\")" ] }, { diff --git a/examples/cp/jupyter/n_queen.ipynb b/examples/cp/jupyter/n_queen.ipynb index a264bce..df71e71 100644 --- a/examples/cp/jupyter/n_queen.ipynb +++ b/examples/cp/jupyter/n_queen.ipynb @@ -204,7 +204,7 @@ }, "outputs": [], "source": [ - "mdl = CpoModel()" + "mdl = CpoModel(name=\"NQueen\")" ] }, { diff --git a/examples/cp/jupyter/sched_square.ipynb b/examples/cp/jupyter/sched_square.ipynb index 3003487..8302716 100644 --- a/examples/cp/jupyter/sched_square.ipynb +++ b/examples/cp/jupyter/sched_square.ipynb @@ -201,7 +201,7 @@ }, "outputs": [], "source": [ - "mdl = CpoModel()" + "mdl = CpoModel(name=\"SchedSquare\")" ] }, { diff --git a/examples/cp/jupyter/sports_scheduling.ipynb b/examples/cp/jupyter/sports_scheduling.ipynb index 01ebd9d..19b6946 100644 --- a/examples/cp/jupyter/sports_scheduling.ipynb +++ b/examples/cp/jupyter/sports_scheduling.ipynb @@ -381,7 +381,7 @@ }, "outputs": [], "source": [ - "mdl = CpoModel(sfile=\"Sports_scheduling\")\n", + "mdl = CpoModel(name=\"SportsScheduling\")\n", "\n", "# Variables of the model\n", "plays = {}\n", diff --git a/examples/cp/jupyter/sudoku.ipynb b/examples/cp/jupyter/sudoku.ipynb index c63310f..772b384 100644 --- a/examples/cp/jupyter/sudoku.ipynb +++ b/examples/cp/jupyter/sudoku.ipynb @@ -364,7 +364,7 @@ }, "outputs": [], "source": [ - "mdl = CpoModel()" + "mdl = CpoModel(name=\"Sudoku\")" ] }, { diff --git a/examples/mp/modeling/diet.py b/examples/mp/modeling/diet.py index 5c7199c..3fb0bbd 100644 --- a/examples/mp/modeling/diet.py +++ b/examples/mp/modeling/diet.py @@ -47,23 +47,24 @@ ("Hotdog", 242.1, 23.5, 2.3, 0, 0, 18, 10.4) ] +Food = namedtuple("Food", ["name", "unit_cost", "qmin", "qmax"]) +Nutrient = namedtuple("Nutrient", ["name", "qmin", "qmax"]) + def build_diet_model(**kwargs): # Create tuples with named fields for foods and nutrients - Food = namedtuple("Food", ["name", "unit_cost", "qmin", "qmax"]) - food = [Food(*f) for f in FOODS] - Nutrient = namedtuple("Nutrient", ["name", "qmin", "qmax"]) + food = [Food(*f) for f in FOODS] nutrients = [Nutrient(*row) for row in NUTRIENTS] food_nutrients = {(fn[0], nutrients[n].name): fn[1 + n] for fn in FOOD_NUTRIENTS for n in range(len(NUTRIENTS))} # Model - mdl = Model("diet", **kwargs) + mdl = Model(name='diet', **kwargs) # Decision variables, limited to be >= Food.qmin and <= Food.qmax - qty = dict((f, mdl.continuous_var(f.qmin, f.qmax, f.name)) for f in food) + qty = {f: mdl.continuous_var(lb=f.qmin, ub=f.qmax, name=f.name) for f in food} # Limit range of nutrients, and mark them as KPIs for n in nutrients: diff --git a/examples/mp/modeling/nurses.py b/examples/mp/modeling/nurses.py index 96d8ca8..a52e1c4 100644 --- a/examples/mp/modeling/nurses.py +++ b/examples/mp/modeling/nurses.py @@ -9,8 +9,6 @@ from docplex.mp.model import Model from docplex.util.environment import get_environment - - # utility to convert a weekday string to an index in 0..6 _all_days = ["monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"] @@ -33,7 +31,9 @@ def __str__(self): # specialized namedtuple to redefine its str() method -class TShift(namedtuple("TShift", ["department", "day", "start_time", "end_time", "min_requirement", "max_requirement"])): +class TShift(namedtuple("TShift", + ["department", "day", "start_time", "end_time", "min_requirement", "max_requirement"])): + def __str__(self): # keep first two characters in department, uppercase dept2 = self.department[0:4].upper() @@ -524,4 +524,4 @@ def build(context=None, **kwargs): # Save the CPLEX solution as "solution.json" program output with get_environment().get_output_stream("solution.json") as fp: - model.solution.export(fp, "json") \ No newline at end of file + model.solution.export(fp, "json") diff --git a/examples/mp/modeling/production.py b/examples/mp/modeling/production.py index 1e5f3fe..43de6ae 100644 --- a/examples/mp/modeling/production.py +++ b/examples/mp/modeling/production.py @@ -33,7 +33,8 @@ def build_production_problem(products, resources, consumptions, **kwargs): mdl.add_constraints((mdl.inside_vars[prod] + mdl.outside_vars[prod] >= prod[1], 'ct_demand_%s' % prod[0]) for prod in products) # --- resource capacity --- - mdl.add_constraints((mdl.sum(mdl.inside_vars[p] * consumptions[p[0], res[0]] for p in products) <= res[1], 'ct_res_%s' %res[0]) for res in resources) + mdl.add_constraints((mdl.sum(mdl.inside_vars[p] * consumptions[p[0], res[0]] for p in products) <= res[1], + 'ct_res_%s' % res[0]) for res in resources) # --- objective --- mdl.total_inside_cost = mdl.sum(mdl.inside_vars[p] * p[2] for p in products) @@ -41,6 +42,7 @@ def build_production_problem(products, resources, consumptions, **kwargs): mdl.minimize(mdl.total_inside_cost + mdl.total_outside_cost) return mdl + def print_production_solution(mdl, products): obj = mdl.objective_value print("* Production model solved with objective: {:g}".format(obj)) diff --git a/examples/mp/modeling/sport_scheduling.py b/examples/mp/modeling/sport_scheduling.py index d975475..e8c6902 100644 --- a/examples/mp/modeling/sport_scheduling.py +++ b/examples/mp/modeling/sport_scheduling.py @@ -60,19 +60,19 @@ def build_sports(context=None): nb_play = {m: nb_intra_divisional if m.is_divisional == 1 else nb_inter_divisional for m in matches} plays = mdl.binary_var_matrix(keys1=matches, keys2=weeks, - name=lambda mw: "play_%d_%d_w%d" % (mw[0].team1, mw[0].team2, mw[1])) + name=lambda mw: "play_%d_%d_w%d" % (mw[0].team1, mw[0].team2, mw[1])) mdl.plays = plays for m in matches: mdl.add_constraint(mdl.sum(plays[m, w] for w in weeks) == nb_play[m], - "correct_nb_games_%d_%d" % (m.team1, m.team2)) + "correct_nb_games_%d_%d" % (m.team1, m.team2)) for w in weeks: # Each team must play exactly once in a week. for t in team_range: max_teams_in_division = (plays[m, w] for m in matches if m.team1 == t or m.team2 == t) mdl.add_constraint(mdl.sum(max_teams_in_division) == 1, - "plays_exactly_once_%d_%s" % (w, t)) + "plays_exactly_once_%d_%s" % (w, t)) # Games between the same teams cannot be on successive weeks. mdl.add_constraints(plays[m, w] + plays[m, w + 1] <= 1 @@ -84,17 +84,18 @@ def build_sports(context=None): m.is_divisional == 1 and (m.team1 == t or m.team2 == t)] mdl.add_constraint(mdl.sum(max_teams_in_division) >= nb_first_half_games, - "in_division_first_half_%s" % t) + "in_division_first_half_%s" % t) # postpone divisional matches as much as possible # we weight each play variable with the square of w. mdl.maximize(mdl.sum(plays[m, w] * w * w for w in weeks for m in matches if m.is_divisional)) return mdl +# a named tuple to store solution +TSolution = namedtuple("TSolution", ["week", "is_divisional", "team1", "team2"]) -def print_sports_solution(mdl): - TSolution = namedtuple("TSolution", ["week", "is_divisional", "team1", "team2"]) +def print_sports_solution(mdl): # iterate with weeks first solution = [TSolution(w, m.is_divisional, mdl.teams[m.team1], mdl.teams[m.team2]) for w in mdl.weeks for m in mdl.matches diff --git a/examples/mp/workflow/cutstock.py b/examples/mp/workflow/cutstock.py index 16f4a5a..630fecf 100644 --- a/examples/mp/workflow/cutstock.py +++ b/examples/mp/workflow/cutstock.py @@ -169,17 +169,17 @@ def print_information(self): AbstractModel.print_information(self) def print_solution(self): - print("| Nb of cuts | Pattern | Detail of pattern (nb of item1, ..., nb of item5) |") - print("| {} |".format("-" * 75)) + print("| Nb of cuts | Pattern | Pattern's detail (# of item1,..., # of item5) |") + print("| {} |".format("-" * 70)) for p in self.patterns: if self.cut_vars[p].solution_value >= 1e-3: pattern_detail = {b.id: self.pattern_item_filled[(a, b)] for (a, b) in self.pattern_item_filled if a == p} print( - "| {:<10g} | {!s:9} | {!s:50} |".format(self.cut_vars[p].solution_value, + "| {:<10g} | {!s:9} | {!s:45} |".format(self.cut_vars[p].solution_value, p, pattern_detail)) - print("| {} |".format("-" * 75)) + print("| {} |".format("-" * 70)) def save_solution_as_json(self, file): solution = [] @@ -224,7 +224,7 @@ def run(self, **kwargs): curr = self.objective_value print('{}> new column generation iteration, best={:g}, curr={:g}'.format(loop_count, best, curr)) duals = self.get_fill_dual_values() - print('{0}> moving duals from master to sub model: {1!s}'.format(loop_count, duals)) + print('{0}> moving duals from master to sub model: {1}'.format(loop_count, map (lambda x: float('%0.2f' % x), duals))) gen_model.update_duals(duals) status = gen_model.solve(**kwargs) if not status: diff --git a/examples/mp/workflow/lagrangian_relaxation.py b/examples/mp/workflow/lagrangian_relaxation.py index e84993a..24ad150 100644 --- a/examples/mp/workflow/lagrangian_relaxation.py +++ b/examples/mp/workflow/lagrangian_relaxation.py @@ -33,89 +33,88 @@ def run_GAP_model(As, Bs, Cs, **kwargs): - mdl = Model('GAP per Wolsey -without- Lagrangian Relaxation', **kwargs) - print("#As={}, #Bs={}, #Cs={}".format(len(As), len(Bs), len(Cs))) - number_of_cs = len(C) - # variables - x_vars = [mdl.binary_var_list(c, name=None) for c in Cs] - - # constraints - mdl.add_constraints(mdl.sum(xv) <= 1 - for xv in x_vars) - - mdl.add_constraints(mdl.sum(x_vars[ii][j] * As[ii][j] for ii in range(number_of_cs)) <= bs - for j, bs in enumerate(Bs)) - - # objective - total_profit = mdl.sum(mdl.sum(c_ij * x_ij for c_ij, x_ij in zip(c_i, x_i)) - for c_i, x_i in zip(Cs, x_vars)) - mdl.maximize(total_profit) - mdl.print_information() - assert mdl.solve(**kwargs) - obj = mdl.objective_value - mdl.print_information() - print("* GAP with no relaxation run OK, best objective is: {:g}".format(obj)) - mdl.end() + with Model('GAP per Wolsey -without- Lagrangian Relaxation', **kwargs) as mdl: + print("#As={}, #Bs={}, #Cs={}".format(len(As), len(Bs), len(Cs))) + number_of_cs = len(C) + # variables + x_vars = [mdl.binary_var_list(c, name=None) for c in Cs] + + # constraints + mdl.add_constraints(mdl.sum(xv) <= 1 + for xv in x_vars) + + mdl.add_constraints(mdl.sum(x_vars[ii][j] * As[ii][j] for ii in range(number_of_cs)) <= bs + for j, bs in enumerate(Bs)) + + # objective + total_profit = mdl.sum(mdl.sum(c_ij * x_ij for c_ij, x_ij in zip(c_i, x_i)) + for c_i, x_i in zip(Cs, x_vars)) + mdl.maximize(total_profit) + mdl.print_information() + assert mdl.solve(**kwargs) + obj = mdl.objective_value + mdl.print_information() + print("* GAP with no relaxation run OK, best objective is: {:g}".format(obj)) return obj def run_GAP_model_with_Lagrangian_relaxation(As, Bs, Cs, max_iters=101, **kwargs): - mdl = Model('GAP per Wolsey -with- Lagrangian Relaxation', **kwargs) - print("#As={}, #Bs={}, #Cs={}".format(len(As), len(Bs), len(Cs))) - c_range = range(len(Cs)) - # variables - x_vars = [mdl.binary_var_list(c, name=None) for c in Cs] - p_vars = [mdl.continuous_var(lb=0) for _ in Cs] # new for relaxation - - - # was mdl.add_constraint(mdl.sum(xVars[i]) <= 1) - mdl.add_constraints(mdl.sum(xv) == 1 - pv for (xv, pv) in izip(x_vars, p_vars)) - - - mdl.add_constraints(mdl.sum(x_vars[ii][j] * As[ii][j] for ii in c_range) <= bs - for j, bs in enumerate(Bs)) - - # lagrangian relaxation loop - eps = 1e-6 - loop_count = 0 - best = 0 - initial_multiplier = 1 - multipliers = [initial_multiplier] * len(Cs) - - total_profit = mdl.sum(mdl.sum(c_ij * x_ij for c_ij, x_ij in zip(c_i, x_i)) for c_i, x_i in zip(Cs, x_vars)) - mdl.add_kpi(total_profit, "Total profit") - - while loop_count <= max_iters: - loop_count += 1 - # rebuilt at each loop iteration - total_penalty = mdl.sum(p_vars[i] * multipliers[i] for i in c_range) - mdl.maximize(total_profit + total_penalty) - ok = mdl.solve(**kwargs) - if not ok: - print("*** solve fails, stopping at iteration: %d" % loop_count) - break - best = mdl.objective_value - penalties = [pv.solution_value for pv in p_vars] - print('%d> new lagrangian iteration, obj=%g, m=%s, p=%s' % (loop_count, best, str(multipliers), str(penalties))) - - do_stop = True - justifier = 0 - for k in c_range: - penalized_violation = penalties[k] * multipliers[k] - if penalized_violation >= eps: - do_stop = False - justifier = penalized_violation + with Model('GAP per Wolsey -with- Lagrangian Relaxation', **kwargs) as mdl: + print("#As={}, #Bs={}, #Cs={}".format(len(As), len(Bs), len(Cs))) + c_range = range(len(Cs)) + # variables + x_vars = [mdl.binary_var_list(c, name=None) for c in Cs] + p_vars = [mdl.continuous_var(lb=0) for _ in Cs] # new for relaxation + + + # was mdl.add_constraint(mdl.sum(xVars[i]) <= 1) + mdl.add_constraints(mdl.sum(xv) == 1 - pv for (xv, pv) in izip(x_vars, p_vars)) + + + mdl.add_constraints(mdl.sum(x_vars[ii][j] * As[ii][j] for ii in c_range) <= bs + for j, bs in enumerate(Bs)) + + # lagrangian relaxation loop + eps = 1e-6 + loop_count = 0 + best = 0 + initial_multiplier = 1 + multipliers = [initial_multiplier] * len(Cs) + + total_profit = mdl.sum(mdl.sum(c_ij * x_ij for c_ij, x_ij in zip(c_i, x_i)) for c_i, x_i in zip(Cs, x_vars)) + mdl.add_kpi(total_profit, "Total profit") + + while loop_count <= max_iters: + loop_count += 1 + # rebuilt at each loop iteration + total_penalty = mdl.sum(p_vars[i] * multipliers[i] for i in c_range) + mdl.maximize(total_profit + total_penalty) + ok = mdl.solve(**kwargs) + if not ok: + print("*** solve fails, stopping at iteration: %d" % loop_count) break - - if do_stop: - print("* Lagrangian relaxation succeeds, best={:g}, penalty={:g}, #iterations={}" - .format(best, total_penalty.solution_value, loop_count)) - break - else: - # update multipliers and start loop again. - scale_factor = 1.0 / float(loop_count) - multipliers = [max(multipliers[i] - scale_factor * penalties[i], 0.) for i in c_range] - print('{}> -- loop continues, m={}, justifier={:g}'.format(loop_count, str(multipliers), justifier)) + best = mdl.objective_value + penalties = [pv.solution_value for pv in p_vars] + print('%d> new lagrangian iteration:\n\t obj=%g, m=%s, p=%s' % (loop_count, best, str(multipliers), str(penalties))) + + do_stop = True + justifier = 0 + for k in c_range: + penalized_violation = penalties[k] * multipliers[k] + if penalized_violation >= eps: + do_stop = False + justifier = penalized_violation + break + + if do_stop: + print("* Lagrangian relaxation succeeds, best={:g}, penalty={:g}, #iterations={}" + .format(best, total_penalty.solution_value, loop_count)) + break + else: + # update multipliers and start loop again. + scale_factor = 1.0 / float(loop_count) + multipliers = [max(multipliers[i] - scale_factor * penalties[i], 0.) for i in c_range] + print('{}> -- loop continues, m={}, justifier={:g}'.format(loop_count, str(multipliers), justifier)) return best diff --git a/examples/mp/workflow/load_balancing.py b/examples/mp/workflow/load_balancing.py index 2eb3cba..69f7751 100644 --- a/examples/mp/workflow/load_balancing.py +++ b/examples/mp/workflow/load_balancing.py @@ -22,7 +22,6 @@ def __str__(self): class LoadBalancingModel(AbstractModel): - def __init__(self, **kwargs): AbstractModel.__init__(self, 'load_balancing', **kwargs) # raw data @@ -74,8 +73,9 @@ def setup_constraints(self): max_proc_per_server = self.max_processes_per_server - mdl.add_constraints(mdl.sum(self.assign_user_to_server_vars[u, s] * u.running for u in all_users) <= max_proc_per_server - for s in all_servers) + mdl.add_constraints( + mdl.sum(self.assign_user_to_server_vars[u, s] * u.running for u in all_users) <= max_proc_per_server + for s in all_servers) # each assignment var is <= active_server(s) for s in all_servers: @@ -102,7 +102,8 @@ def setup_objective(self): for s in self.servers: ct_name = 'ct_define_max_sleeping_%s' % s mdl.add_constraint( - mdl.sum(self.assign_user_to_server_vars[u, s] * u.sleeping for u in self.users) <= max_sleeping_workload, + mdl.sum( + self.assign_user_to_server_vars[u, s] * u.sleeping for u in self.users) <= max_sleeping_workload, ct_name) mdl.add_kpi(max_sleeping_workload, "Max sleeping workload") self.max_sleeping_workload = max_sleeping_workload @@ -142,28 +143,28 @@ def save_solution_as_json(self, json_file): # active server active_servers = sorted([s for s in mdl.servers if mdl.active_var_by_server[s].solution_value == 1]) solution_dict["active servers"] = active_servers - + # sleeping processes by server sleeping_processes = {} for s in active_servers: sleeping = sum(self.assign_user_to_server_vars[u, s].solution_value * u.sleeping for u in self.users) sleeping_processes[s] = sleeping solution_dict["sleeping processes by server"] = sleeping_processes - + # user assignment user_assignment = [] for (u, s) in sorted(mdl.assign_user_to_server_vars): if mdl.assign_user_to_server_vars[(u, s)].solution_value == 1: - n = {} - n['user'] = u.id - n['server'] = s - n['migration'] = "yes" if mdl._is_migration(u, s) else "no" + n = { + 'user': u.id, + 'server': s, + 'migration': "yes" if mdl._is_migration(u, s) else "no" + } user_assignment.append(n) solution_dict['user assignment'] = user_assignment json_file.write(json.dumps(solution_dict, indent=3).encode('utf-8')) - SERVERS = ["server002", "server003", "server001", "server006", "server007", "server004", "server005"] USERS = [("user013", 2, 1, "server002"), @@ -252,7 +253,6 @@ def save_solution_as_json(self, json_file): class DefaultLoadBalancingModel(LoadBalancingModel): - def __init__(self, context=None, **kwargs): LoadBalancingModel.__init__(self, context=context, **kwargs) self.load_data(SERVERS, USERS) @@ -277,7 +277,6 @@ def __init__(self, context=None, **kwargs): url = None # put your url here key = None # put your api key here - lbm = DefaultLoadBalancingModel() # Run the model. If a key has been specified above, the model will run on