def solve(dat): """ core solving routine :param dat: a good ticdat for the input_schema :return: a good ticdat for the solution_schema, or None """ assert input_schema.good_tic_dat_object(dat) assert not input_schema.find_foreign_key_failures(dat) assert not input_schema.find_data_type_failures(dat) mdl = Model(_model_type, "netflow") flow = {(h, i, j): mdl.add_var(name='flow_%s_%s_%s' % (h, i, j)) for h, i, j in dat.cost if (i,j) in dat.arcs} flowslice = Slicer(flow) # Arc Capacity constraints for i_,j_ in dat.arcs: mdl.add_constraint(mdl.sum(flow[h,i,j] for h,i,j in flowslice.slice('*',i_, j_)) <= dat.arcs[i_,j_]["Capacity"], name='cap_%s_%s' % (i_, j_)) # Flow conservation constraints. Constraints are generated only for relevant pairs. # So we generate a conservation of flow constraint if there is negative or positive inflow # quantity, or at least one inbound flow variable, or at least one outbound flow variable. for h,j in set(k for k,v in dat.inflow.items() if abs(v["Quantity"]) > 0)\ .union({(h,i) for h,i,j in flow}, {(h,j) for h,i,j in flow}): mdl.add_constraint( mdl.sum(flow[h_,i_,j_] for h_,i_,j_ in flowslice.slice(h,'*',j)) + dat.inflow.get((h,j), {"Quantity":0})["Quantity"] == mdl.sum(flow[h_,j_,i_] for h_,j_,i_ in flowslice.slice(h, j, '*')), name='node_%s_%s' % (h, j)) mdl.set_objective(mdl.sum(flow * dat.cost[h, i, j]["Cost"] for (h, i, j), flow in flow.items())) # Compute optimal solution if mdl.optimize(): rtn = solution_schema.TicDat() for (h, i, j),var in flow.items(): if mdl.get_solution_value(var) > 0: rtn.flow[h, i, j] = mdl.get_solution_value(var) rtn.parameters["Total Cost"] = sum(dat.cost[h, i, j]["Cost"] * r["Quantity"] for (h, i, j), r in rtn.flow.items()) return rtn
def dietSolver(modelType): tdf = TicDatFactory(**dietSchema()) addDietForeignKeys(tdf) addDietDataTypes(tdf) dat = tdf.copy_tic_dat(dietData()) assert not tdf.find_data_type_failures(dat) and not tdf.find_foreign_key_failures(dat) mdl = Model(modelType, "diet") nutrition = {} for c,n in dat.categories.items() : nutrition[c] = mdl.add_var(lb=n["minNutrition"], ub=n["maxNutrition"], name=c) # Create decision variables for the foods to buy buy = {} for f in dat.foods: buy[f] = mdl.add_var(name=f) # Nutrition constraints for c in dat.categories: mdl.add_constraint(mdl.sum(dat.nutritionQuantities[f,c]["qty"] * buy[f] for f in dat.foods) == nutrition[c], name = c) mdl.set_objective(mdl.sum(buy[f] * c["cost"] for f,c in dat.foods.items())) if mdl.optimize(): solutionFactory = TicDatFactory( parameters = [[],["totalCost"]], buyFood = [["food"],["qty"]], consumeNutrition = [["category"],["qty"]]) sln = solutionFactory.TicDat() for f,x in buy.items(): if mdl.get_solution_value(x) > 0.0001: sln.buyFood[f] = mdl.get_solution_value(x) for c,x in nutrition.items(): sln.consumeNutrition[c] = mdl.get_solution_value(x) return sln, sum(dat.foods[f]["cost"] * r["qty"] for f,r in sln.buyFood.items())
def netflowSolver(modelType): tdf = TicDatFactory(**netflowSchema()) addNetflowForeignKeys(tdf) addNetflowDataTypes(tdf) dat = tdf.copy_tic_dat(netflowData()) assert not tdf.find_data_type_failures(dat) and not tdf.find_foreign_key_failures(dat) mdl = Model(modelType, "netflow") flow = {} for h, i, j in dat.cost: if (i,j) in dat.arcs: flow[h,i,j] = mdl.add_var(name='flow_%s_%s_%s' % (h, i, j)) flowslice = Slicer(flow) for i_,j_ in dat.arcs: mdl.add_constraint(mdl.sum(flow[h,i,j] for h,i,j in flowslice.slice('*',i_, j_)) <= dat.arcs[i_,j_]["capacity"], name = 'cap_%s_%s' % (i_, j_)) for h_,j_ in set(k for k,v in dat.inflow.items() if abs(v["quantity"]) > 0).union( {(h,i) for h,i,j in flow}, {(h,j) for h,i,j in flow}) : mdl.add_constraint( mdl.sum(flow[h,i,j] for h,i,j in flowslice.slice(h_,'*',j_)) + dat.inflow.get((h_,j_), {"quantity":0})["quantity"] == mdl.sum(flow[h,i,j] for h,i,j in flowslice.slice(h_, j_, '*')), name = 'node_%s_%s' % (h_, j_)) mdl.set_objective(mdl.sum(flow * dat.cost[h, i, j]["cost"] for (h, i, j),flow in flow.items())) if mdl.optimize(): solutionFactory = TicDatFactory( flow = [["commodity", "source", "destination"], ["quantity"]]) if mdl.optimize(): rtn = solutionFactory.TicDat() for (h, i, j),var in flow.items(): if mdl.get_solution_value(var) > 0: rtn.flow[h,i,j] = mdl.get_solution_value(var) return rtn, sum(dat.cost[h,i,j]["cost"] * r["quantity"] for (h,i,j),r in rtn.flow.items())
def solve(dat): """ core solving routine :param dat: a good ticdat for the input_schema :return: a good ticdat for the solution_schema, or None """ assert input_schema.good_tic_dat_object(dat) assert not input_schema.find_foreign_key_failures(dat) assert not input_schema.find_data_type_failures(dat) assert not input_schema.find_data_row_failures(dat) mdl = Model(_model_type, "diet") nutrition = { c: mdl.add_var(lb=n["Min Nutrition"], ub=n["Max Nutrition"], name=c) for c, n in dat.categories.items() } # Create decision variables for the foods to buy buy = {f: mdl.add_var(name=f) for f in dat.foods} # Nutrition constraints for c in dat.categories: mdl.add_constraint(mdl.sum(dat.nutrition_quantities[f, c]["Quantity"] * buy[f] for f in dat.foods) == nutrition[c], name=c) mdl.set_objective(mdl.sum(buy[f] * c["Cost"] for f, c in dat.foods.items())) if mdl.optimize(): sln = solution_schema.TicDat() for f, x in buy.items(): if mdl.get_solution_value(x) > 0: sln.buy_food[f] = mdl.get_solution_value(x) for c, x in nutrition.items(): sln.consume_nutrition[c] = mdl.get_solution_value(x) sln.parameters['Total Cost'] = sum(dat.foods[f]["Cost"] * r["Quantity"] for f, r in sln.buy_food.items()) return sln
def solve(dat): assert input_schema.good_tic_dat_object(dat) assert not input_schema.find_foreign_key_failures(dat) assert not input_schema.find_data_type_failures(dat) expected_draft_position = {} # for our purposes, its fine to assume all those drafted by someone else are drafted # prior to any players drafted by me for player_name in sorted( dat.players, key=lambda _p: { "Un-drafted": dat.players[_p]["Average Draft Position"], "Drafted By Me": -1, "Drafted By Someone Else": -2 }[dat.players[_p]["Draft Status"]]): expected_draft_position[player_name] = len(expected_draft_position) + 1 assert max(expected_draft_position.values()) == len( set(expected_draft_position.values())) == len(dat.players) assert min(expected_draft_position.values()) == 1 already_drafted_by_me = { player_name for player_name, row in dat.players.items() if row["Draft Status"] == "Drafted By Me" } can_be_drafted_by_me = { player_name for player_name, row in dat.players.items() if row["Draft Status"] != "Drafted By Someone Else" } m = Model(_model_type, 'fantop') my_starters = { player_name: m.add_var(type="binary", name="starter_%s" % player_name) for player_name in can_be_drafted_by_me } my_reserves = { player_name: m.add_var(type="binary", name="reserve_%s" % player_name) for player_name in can_be_drafted_by_me } for player_name in can_be_drafted_by_me: if player_name in already_drafted_by_me: m.add_constraint(my_starters[player_name] + my_reserves[player_name] == 1, name="already_drafted_%s" % player_name) else: m.add_constraint( my_starters[player_name] + my_reserves[player_name] <= 1, name="cant_draft_twice_%s" % player_name) for i, draft_position in enumerate(sorted(dat.my_draft_positions)): m.add_constraint(m.sum( my_starters[player_name] + my_reserves[player_name] for player_name in can_be_drafted_by_me if expected_draft_position[player_name] < draft_position) <= i, name="at_most_%s_can_be_ahead_of_%s" % (i, draft_position)) my_draft_size = m.sum(my_starters[player_name] + my_reserves[player_name] for player_name in can_be_drafted_by_me) m.add_constraint(my_draft_size >= len(already_drafted_by_me) + 1, name="need_to_extend_by_at_least_one") m.add_constraint(my_draft_size <= len(dat.my_draft_positions), name="cant_exceed_draft_total") for position, row in dat.roster_requirements.items(): players = { player_name for player_name in can_be_drafted_by_me if dat.players[player_name]["Position"] == position } starters = m.sum(my_starters[player_name] for player_name in players) reserves = m.sum(my_reserves[player_name] for player_name in players) m.add_constraint(starters >= row["Min Num Starters"], name="min_starters_%s" % position) m.add_constraint(starters <= row["Max Num Starters"], name="max_starters_%s" % position) m.add_constraint(reserves >= row["Min Num Reserve"], name="min_reserve_%s" % position) m.add_constraint(reserves <= row["Max Num Reserve"], name="max_reserve_%s" % position) if "Maximum Number of Flex Starters" in dat.parameters: players = { player_name for player_name in can_be_drafted_by_me if dat.roster_requirements[dat.players[player_name]["Position"]] ["Flex Status"] == "Flex Eligible" } m.add_constraint( m.sum(my_starters[player_name] for player_name in players) <= dat.parameters["Maximum Number of Flex Starters"]["Value"], name="max_flex") starter_weight = dat.parameters["Starter Weight"][ "Value"] if "Starter Weight" in dat.parameters else 1 reserve_weight = dat.parameters["Reserve Weight"][ "Value"] if "Reserve Weight" in dat.parameters else 1 m.set_objective(m.sum(dat.players[player_name]["Expected Points"] * (my_starters[player_name] * starter_weight + my_reserves[player_name] * reserve_weight) for player_name in can_be_drafted_by_me), sense="maximize") if not m.optimize(): print("No draft at all is possible!") return sln = solution_schema.TicDat() def almostone(x): return abs(m.get_solution_value(x) - 1) < 0.0001 picked = sorted([ player_name for player_name in can_be_drafted_by_me if almostone(my_starters[player_name]) or almostone(my_reserves[player_name]) ], key=lambda _p: expected_draft_position[_p]) assert len(picked) <= len(dat.my_draft_positions) if len(picked) < len(dat.my_draft_positions): print( "Your model is over-constrained, and thus only a partial draft was possible" ) draft_yield = 0 for player_name, draft_position in zip(picked, sorted(dat.my_draft_positions)): draft_yield += dat.players[player_name]["Expected Points"] * \ (starter_weight if almostone(my_starters[player_name]) else reserve_weight) assert draft_position <= expected_draft_position[player_name] sln.my_draft[player_name]["Draft Position"] = draft_position sln.my_draft[player_name]["Position"] = dat.players[player_name][ "Position"] sln.my_draft[player_name][ "Planned Or Actual"] = "Actual" if player_name in already_drafted_by_me else "Planned" sln.my_draft[player_name]["Starter Or Reserve"] = \ "Starter" if almostone(my_starters[player_name]) else "Reserve" sln.parameters["Total Yield"] = draft_yield sln.parameters["Feasible"] = len(sln.my_draft) == len( dat.my_draft_positions) return sln
def _testFantop(modelType, sqlFile): dataFactory = TicDatFactory(parameters=[["Key"], ["Value"]], players=[['Player Name'], [ 'Position', 'Average Draft Position', 'Expected Points', 'Draft Status' ]], roster_requirements=[['Position'], [ 'Min Num Starters', 'Max Num Starters', 'Min Num Reserve', 'Max Num Reserve', 'Flex Status' ]], my_draft_positions=[['Draft Position'], []]) # add foreign key constraints (optional, but helps with preventing garbage-in, garbage-out) dataFactory.add_foreign_key("players", "roster_requirements", ['Position', 'Position']) # set data types (optional, but helps with preventing garbage-in, garbage-out) dataFactory.set_data_type("parameters", "Key", number_allowed=False, strings_allowed=[ "Starter Weight", "Reserve Weight", "Maximum Number of Flex Starters" ]) dataFactory.set_data_type("parameters", "Value", min=0, max=float("inf"), inclusive_min=True, inclusive_max=False) dataFactory.set_data_type("players", "Average Draft Position", min=0, max=float("inf"), inclusive_min=False, inclusive_max=False) dataFactory.set_data_type("players", "Expected Points", min=-float("inf"), max=float("inf"), inclusive_min=False, inclusive_max=False) dataFactory.set_data_type("players", "Draft Status", strings_allowed=[ "Un-drafted", "Drafted By Me", "Drafted By Someone Else" ]) for fld in ("Min Num Starters", "Min Num Reserve", "Max Num Reserve"): dataFactory.set_data_type("roster_requirements", fld, min=0, max=float("inf"), inclusive_min=True, inclusive_max=False, must_be_int=True) dataFactory.set_data_type("roster_requirements", "Max Num Starters", min=0, max=float("inf"), inclusive_min=False, inclusive_max=True, must_be_int=True) dataFactory.set_data_type( "roster_requirements", "Flex Status", number_allowed=False, strings_allowed=["Flex Eligible", "Flex Ineligible"]) dataFactory.set_data_type("my_draft_positions", "Draft Position", min=0, max=float("inf"), inclusive_min=False, inclusive_max=False, must_be_int=True) solutionFactory = TicDatFactory(my_draft=[[ 'Player Name' ], [ 'Draft Position', 'Position', 'Planned Or Actual', 'Starter Or Reserve' ]]) dat = dataFactory.sql.create_tic_dat_from_sql(os.path.join( _codeDir(), sqlFile), freeze_it=True) assert dataFactory.good_tic_dat_object(dat) assert not dataFactory.find_foreign_key_failures(dat) assert not dataFactory.find_data_type_failures(dat) expected_draft_position = {} # for our purposes, its fine to assume all those drafted by someone else are drafted # prior to any players drafted by me for player_name in sorted( dat.players, key=lambda _p: { "Un-drafted": dat.players[_p]["Average Draft Position"], "Drafted By Me": -1, "Drafted By Someone Else": -2 }[dat.players[_p]["Draft Status"]]): expected_draft_position[player_name] = len(expected_draft_position) + 1 assert max(expected_draft_position.values()) == len( set(expected_draft_position.values())) == len(dat.players) assert min(expected_draft_position.values()) == 1 already_drafted_by_me = { player_name for player_name, row in dat.players.items() if row["Draft Status"] == "Drafted By Me" } can_be_drafted_by_me = { player_name for player_name, row in dat.players.items() if row["Draft Status"] != "Drafted By Someone Else" } m = Model(modelType, 'fantop') my_starters = { player_name: m.add_var(type="binary", name="starter_%s" % player_name) for player_name in can_be_drafted_by_me } my_reserves = { player_name: m.add_var(type="binary", name="reserve_%s" % player_name) for player_name in can_be_drafted_by_me } for player_name in can_be_drafted_by_me: if player_name in already_drafted_by_me: m.add_constraint(my_starters[player_name] + my_reserves[player_name] == 1, name="already_drafted_%s" % player_name) else: m.add_constraint( my_starters[player_name] + my_reserves[player_name] <= 1, name="cant_draft_twice_%s" % player_name) for i, draft_position in enumerate(sorted(dat.my_draft_positions)): m.add_constraint(m.sum( my_starters[player_name] + my_reserves[player_name] for player_name in can_be_drafted_by_me if expected_draft_position[player_name] < draft_position) <= i, name="at_most_%s_can_be_ahead_of_%s" % (i, draft_position)) my_draft_size = m.sum(my_starters[player_name] + my_reserves[player_name] for player_name in can_be_drafted_by_me) m.add_constraint(my_draft_size >= len(already_drafted_by_me) + 1, name="need_to_extend_by_at_least_one") m.add_constraint(my_draft_size <= len(dat.my_draft_positions), name="cant_exceed_draft_total") for position, row in dat.roster_requirements.items(): players = { player_name for player_name in can_be_drafted_by_me if dat.players[player_name]["Position"] == position } starters = m.sum(my_starters[player_name] for player_name in players) reserves = m.sum(my_reserves[player_name] for player_name in players) m.add_constraint(starters >= row["Min Num Starters"], name="min_starters_%s" % position) m.add_constraint(starters <= row["Max Num Starters"], name="max_starters_%s" % position) m.add_constraint(reserves >= row["Min Num Reserve"], name="min_reserve_%s" % position) m.add_constraint(reserves <= row["Max Num Reserve"], name="max_reserve_%s" % position) if "Maximum Number of Flex Starters" in dat.parameters: players = { player_name for player_name in can_be_drafted_by_me if dat.roster_requirements[dat.players[player_name]["Position"]] ["Flex Status"] == "Flex Eligible" } m.add_constraint( m.sum(my_starters[player_name] for player_name in players) <= dat.parameters["Maximum Number of Flex Starters"]["Value"], name="max_flex") starter_weight = dat.parameters["Starter Weight"][ "Value"] if "Starter Weight" in dat.parameters else 1 reserve_weight = dat.parameters["Reserve Weight"][ "Value"] if "Reserve Weight" in dat.parameters else 1 m.set_objective(m.sum(dat.players[player_name]["Expected Points"] * (my_starters[player_name] * starter_weight + my_reserves[player_name] * reserve_weight) for player_name in can_be_drafted_by_me), sense="maximize") if not m.optimize(): return sln = solutionFactory.TicDat() def almostone(x): return abs(m.get_solution_value(x) - 1) < 0.0001 picked = sorted([ player_name for player_name in can_be_drafted_by_me if almostone(my_starters[player_name]) or almostone(my_reserves[player_name]) ], key=lambda _p: expected_draft_position[_p]) assert len(picked) <= len(dat.my_draft_positions) if len(picked) < len(dat.my_draft_positions): print( "Your model is over-constrained, and thus only a partial draft was possible" ) draft_yield = 0 for player_name, draft_position in zip(picked, sorted(dat.my_draft_positions)): draft_yield += dat.players[player_name]["Expected Points"] * \ (starter_weight if almostone(my_starters[player_name]) else reserve_weight) assert draft_position <= expected_draft_position[player_name] sln.my_draft[player_name]["Draft Position"] = draft_position sln.my_draft[player_name]["Position"] = dat.players[player_name][ "Position"] sln.my_draft[player_name][ "Planned Or Actual"] = "Actual" if player_name in already_drafted_by_me else "Planned" sln.my_draft[player_name]["Starter Or Reserve"] = \ "Starter" if almostone(my_starters[player_name]) else "Reserve" return sln, draft_yield
def _testParameters(self, modelType): mdl = Model(modelType, "parameters") mdl.set_parameters(MIP_Gap=0.01)
def solve(dat, progress): assert isinstance(progress, Progress) assert input_schema.good_tic_dat_object(dat) assert not input_schema.find_foreign_key_failures(dat) assert not input_schema.find_data_type_failures(dat) full_parameters = input_schema.create_full_parameters_dict(dat) def get_distance(x, y): if (x, y) in dat.distance: return dat.distance[x, y]["Distance"] if (y, x) in dat.distance: return dat.distance[y, x]["Distance"] return float("inf") def can_assign(x, y): return dat.sites[y]["Center Status"] == "Can Be Center" \ and get_distance(x,y)<float("inf") m = Model(model_name="cog", model_type=full_parameters["Core Model Type"]) assign_vars = {(n, assigned_to): m.add_var(type="binary", name="%s_%s" % (n, assigned_to)) for n in dat.sites for assigned_to in dat.sites if can_assign(n, assigned_to)} open_vars = { n: m.add_var(type="binary", name="open_%s" % n) for n in dat.sites if dat.sites[n]["Center Status"] == "Can Be Center" } assert open_vars, "nothing can be center" assign_slicer = Slicer(assign_vars) for n, r in dat.sites.items(): if r["Demand"] > 0: m.add_constraint(m.sum( assign_vars[n, assign_to] for _, assign_to in assign_slicer.slice(n, "*")) == 1, name="must_assign_%s" % n) for assigned_to, r in dat.sites.items(): if r["Center Status"] == "Can Be Center": _assign_vars = [ assign_vars[n, assigned_to] for n, _ in assign_slicer.slice("*", assigned_to) ] # this is the weak formulation m.add_constraint(m.sum(_assign_vars) <= len(_assign_vars) * open_vars[assigned_to], name="weak_force_open%s" % assigned_to) number_of_centroids = full_parameters["Number of Centroids"] m.add_constraint(m.sum(v for v in open_vars.values()) == number_of_centroids, name="numCentroids") m.set_parameters(MIP_Gap=full_parameters["MIP Gap"]) m.set_objective(m.sum(var * get_distance(n, assigned_to) * dat.sites[n]["Demand"] for (n, assigned_to), var in assign_vars.items()), sense="minimize") if full_parameters["Core Model Type"] == "cplex": progress.add_cplex_listener("COG Optimization", m.core_model) worked = m.optimize(*( [progress.gurobi_call_back_factory("COG Optimization", m.core_model)] if full_parameters["Core Model Type"] == "gurobi" else [])) assert worked, "testing model set up only for success" sln = solution_schema.TicDat() if full_parameters["Core Model Type"] == "gurobi": sln.parameters["Lower Bound"] = getattr(m.core_model, "objBound", m.core_model.objVal) sln.parameters["Upper Bound"] = m.core_model.objVal else: lb = m.core_model.solve_details.best_bound sln.parameters["Lower Bound"] = worked.get_objective_value() if isnan( lb) else lb sln.parameters["Upper Bound"] = worked.get_objective_value() def almostone(x): return abs(x - 1) < 0.0001 for (n, assigned_to), var in assign_vars.items(): if almostone(m.get_solution_value(var)): sln.assignments[n, assigned_to] = {} for n, var in open_vars.items(): if almostone(m.get_solution_value(var)): sln.openings[n] = {} return sln